tangled
alpha
login
or
join now
tranquil.farm
/
tranquil-pds
146
fork
atom
Our Personal Data Server from scratch!
tranquil.farm
oauth
atproto
pds
rust
postgresql
objectstorage
fun
146
fork
atom
overview
issues
18
pulls
2
pipelines
Sharded filesystem subdirs
oyster.cafe
2 months ago
eda46b0f
8315d691
+100
-1
1 changed file
expand all
collapse all
unified
split
crates
tranquil-storage
src
lib.rs
+100
-1
crates/tranquil-storage/src/lib.rs
reviewed
···
19
19
20
20
const MIN_PART_SIZE: usize = 5 * 1024 * 1024;
21
21
const EXDEV: i32 = 18;
22
22
+
const CID_SHARD_PREFIX_LEN: usize = 9;
23
23
+
24
24
+
fn split_cid_path(key: &str) -> Option<(&str, &str)> {
25
25
+
let is_cid = key.get(..3).map_or(false, |p| p.eq_ignore_ascii_case("baf"));
26
26
+
(key.len() > CID_SHARD_PREFIX_LEN && is_cid)
27
27
+
.then(|| key.split_at(CID_SHARD_PREFIX_LEN))
28
28
+
}
22
29
23
30
fn validate_key(key: &str) -> Result<(), StorageError> {
24
31
let dominated_by_traversal = key
···
483
490
484
491
fn resolve_path(&self, key: &str) -> Result<PathBuf, StorageError> {
485
492
validate_key(key)?;
486
486
-
Ok(self.base_path.join(key))
493
493
+
Ok(split_cid_path(key).map_or_else(
494
494
+
|| self.base_path.join(key),
495
495
+
|(dir, file)| self.base_path.join(dir).join(file),
496
496
+
))
487
497
}
488
498
489
499
async fn atomic_write(&self, path: &Path, data: &[u8]) -> Result<(), StorageError> {
···
751
761
}
752
762
753
763
impl<T> Pipe for T {}
764
764
+
765
765
+
#[cfg(test)]
766
766
+
mod tests {
767
767
+
use super::*;
768
768
+
769
769
+
#[test]
770
770
+
fn split_path_from_raw_blob_cid() {
771
771
+
let cid = "bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku";
772
772
+
assert_eq!(
773
773
+
split_cid_path(cid),
774
774
+
Some(("bafkreihd", "wdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku"))
775
775
+
);
776
776
+
}
777
777
+
778
778
+
#[test]
779
779
+
fn split_path_from_dag_cbor_cid() {
780
780
+
let cid = "bafyreigdmqpykrgxyaxtlafqpqhzrb7qy2rh75nldvfd4tucqmqqme5yje";
781
781
+
assert_eq!(
782
782
+
split_cid_path(cid),
783
783
+
Some(("bafyreigd", "mqpykrgxyaxtlafqpqhzrb7qy2rh75nldvfd4tucqmqqme5yje"))
784
784
+
);
785
785
+
}
786
786
+
787
787
+
#[test]
788
788
+
fn no_split_for_temp_keys() {
789
789
+
assert_eq!(split_cid_path("temp/abc123"), None);
790
790
+
}
791
791
+
792
792
+
#[test]
793
793
+
fn no_split_for_short_keys() {
794
794
+
assert_eq!(split_cid_path("bafkreihd"), None);
795
795
+
assert_eq!(split_cid_path("bafkrei"), None);
796
796
+
assert_eq!(split_cid_path("baf"), None);
797
797
+
assert_eq!(split_cid_path("ba"), None);
798
798
+
assert_eq!(split_cid_path(""), None);
799
799
+
}
800
800
+
801
801
+
#[test]
802
802
+
fn no_split_for_non_cid_keys() {
803
803
+
assert_eq!(split_cid_path("something/else/entirely"), None);
804
804
+
assert_eq!(split_cid_path("Qmabcdefghijklmnop"), None);
805
805
+
}
806
806
+
807
807
+
#[test]
808
808
+
fn split_cid_case_insensitive() {
809
809
+
let upper = "BAFKREIHDWDCEFGH4DQKJV67UZCMW7OJEE6XEDZDETOJUZJEVTENXQUVYKU";
810
810
+
let mixed = "BaFkReIhDwDcEfGh4DqKjV67UzCmW7OjEe6XeDzDeTojUzJevTeNxQuVyKu";
811
811
+
assert_eq!(
812
812
+
split_cid_path(upper),
813
813
+
Some(("BAFKREIHD", "WDCEFGH4DQKJV67UZCMW7OJEE6XEDZDETOJUZJEVTENXQUVYKU"))
814
814
+
);
815
815
+
assert_eq!(
816
816
+
split_cid_path(mixed),
817
817
+
Some(("BaFkReIhD", "wDcEfGh4DqKjV67UzCmW7OjEe6XeDzDeTojUzJevTeNxQuVyKu"))
818
818
+
);
819
819
+
}
820
820
+
821
821
+
#[test]
822
822
+
fn split_at_minimum_length() {
823
823
+
let cid = "bafkreihdx";
824
824
+
assert_eq!(split_cid_path(cid), Some(("bafkreihd", "x")));
825
825
+
}
826
826
+
827
827
+
#[test]
828
828
+
fn resolve_path_shards_cid_keys() {
829
829
+
let base = PathBuf::from("/blobs");
830
830
+
let cid = "bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku";
831
831
+
832
832
+
let expected = PathBuf::from("/blobs/bafkreihd/wdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku");
833
833
+
let result = split_cid_path(cid).map_or_else(
834
834
+
|| base.join(cid),
835
835
+
|(dir, file)| base.join(dir).join(file),
836
836
+
);
837
837
+
assert_eq!(result, expected);
838
838
+
}
839
839
+
840
840
+
#[test]
841
841
+
fn resolve_path_no_shard_for_temp() {
842
842
+
let base = PathBuf::from("/blobs");
843
843
+
let key = "temp/abc123";
844
844
+
845
845
+
let expected = PathBuf::from("/blobs/temp/abc123");
846
846
+
let result = split_cid_path(key).map_or_else(
847
847
+
|| base.join(key),
848
848
+
|(dir, file)| base.join(dir).join(file),
849
849
+
);
850
850
+
assert_eq!(result, expected);
851
851
+
}
852
852
+
}