1use std::borrow::Cow;
8use std::fs::File;
9use std::io::Error;
10use std::io::ErrorKind;
11use std::io::Result;
12use std::path::Path;
13use std::sync::Arc;
14
15use derivative::Derivative;
16use tempfile::NamedTempFile;
17
18use crate::plain::Cache as PlainCache;
19use crate::sharded::Cache as ShardedCache;
20use crate::Key;
21use crate::ReadOnlyCache;
22use crate::ReadOnlyCacheBuilder;
23
24type ConsistencyChecker = Arc<
27 dyn Fn(&mut File, &mut File) -> Result<()>
28 + Sync
29 + Send
30 + std::panic::RefUnwindSafe
31 + std::panic::UnwindSafe,
32>;
33
34trait FullCache:
37 std::fmt::Debug + Sync + Send + std::panic::RefUnwindSafe + std::panic::UnwindSafe
38{
39 fn get(&self, key: Key) -> Result<Option<File>>;
44
45 fn temp_dir(&self, key: Key) -> Result<Cow<Path>>;
48
49 fn set(&self, key: Key, value: &Path) -> Result<()>;
55
56 fn put(&self, key: Key, value: &Path) -> Result<()>;
63
64 fn touch(&self, key: Key) -> Result<bool>;
68}
69
70impl FullCache for PlainCache {
71 fn get(&self, key: Key) -> Result<Option<File>> {
72 PlainCache::get(self, key.name)
73 }
74
75 fn temp_dir(&self, _key: Key) -> Result<Cow<Path>> {
76 PlainCache::temp_dir(self)
77 }
78
79 fn set(&self, key: Key, value: &Path) -> Result<()> {
80 PlainCache::set(self, key.name, value)
81 }
82
83 fn put(&self, key: Key, value: &Path) -> Result<()> {
84 PlainCache::put(self, key.name, value)
85 }
86
87 fn touch(&self, key: Key) -> Result<bool> {
88 PlainCache::touch(self, key.name)
89 }
90}
91
92impl FullCache for ShardedCache {
93 fn get(&self, key: Key) -> Result<Option<File>> {
94 ShardedCache::get(self, key)
95 }
96
97 fn temp_dir(&self, key: Key) -> Result<Cow<Path>> {
98 ShardedCache::temp_dir(self, Some(key))
99 }
100
101 fn set(&self, key: Key, value: &Path) -> Result<()> {
102 ShardedCache::set(self, key, value)
103 }
104
105 fn put(&self, key: Key, value: &Path) -> Result<()> {
106 ShardedCache::put(self, key, value)
107 }
108
109 fn touch(&self, key: Key) -> Result<bool> {
110 ShardedCache::touch(self, key)
111 }
112}
113
114#[derive(Derivative)]
119#[derivative(Debug)]
120pub struct CacheBuilder {
121 write_side: Option<Arc<dyn FullCache>>,
122 auto_sync: bool,
123
124 #[derivative(Debug = "ignore")]
125 consistency_checker: Option<ConsistencyChecker>,
126
127 read_side: ReadOnlyCacheBuilder,
128}
129
130impl Default for CacheBuilder {
131 fn default() -> CacheBuilder {
132 CacheBuilder {
133 write_side: None,
134 auto_sync: true,
135 consistency_checker: None,
136 read_side: Default::default(),
137 }
138 }
139}
140
141#[derive(Clone, Derivative)]
155#[derivative(Debug)]
156pub struct Cache {
157 write_side: Option<Arc<dyn FullCache>>,
160 auto_sync: bool,
163
164 #[derivative(Debug = "ignore")]
168 consistency_checker: Option<ConsistencyChecker>,
169
170 read_side: ReadOnlyCache,
174}
175
176impl Default for Cache {
177 fn default() -> Cache {
178 Cache {
179 write_side: None,
180 auto_sync: true,
181 consistency_checker: None,
182 read_side: Default::default(),
183 }
184 }
185}
186
187pub enum CacheHit<'a> {
190 Primary(&'a mut File),
193 Secondary(&'a mut File),
196}
197
198pub enum CacheHitAction {
200 Accept,
202 Promote,
205 Replace,
207}
208
209impl CacheBuilder {
210 pub fn new() -> Self {
212 Self::default()
213 }
214
215 pub fn consistency_checker(
224 &mut self,
225 checker: impl Fn(&mut File, &mut File) -> Result<()>
226 + Sync
227 + Send
228 + std::panic::RefUnwindSafe
229 + std::panic::UnwindSafe
230 + Sized
231 + 'static,
232 ) -> &mut Self {
233 self.arc_consistency_checker(Some(Arc::new(checker)))
234 }
235
236 pub fn byte_equality_checker(&mut self) -> &mut Self {
241 self.consistency_checker(crate::byte_equality_checker)
242 }
243
244 pub fn panicking_byte_equality_checker(&mut self) -> &mut Self {
249 self.consistency_checker(crate::panicking_byte_equality_checker)
250 }
251
252 pub fn clear_consistency_checker(&mut self) -> &mut Self {
254 self.arc_consistency_checker(None)
255 }
256
257 #[allow(clippy::type_complexity)] pub fn arc_consistency_checker(
261 &mut self,
262 checker: Option<
263 Arc<
264 dyn Fn(&mut File, &mut File) -> Result<()>
265 + Sync
266 + Send
267 + std::panic::RefUnwindSafe
268 + std::panic::UnwindSafe,
269 >,
270 >,
271 ) -> &mut Self {
272 self.consistency_checker = checker.clone();
273 self.read_side.arc_consistency_checker(checker);
274 self
275 }
276
277 pub fn writer(
282 &mut self,
283 path: impl AsRef<Path>,
284 num_shards: usize,
285 total_capacity: usize,
286 ) -> &mut Self {
287 if num_shards <= 1 {
288 self.plain_writer(path, total_capacity)
289 } else {
290 self.sharded_writer(path, num_shards, total_capacity)
291 }
292 }
293
294 pub fn plain_writer(&mut self, path: impl AsRef<Path>, capacity: usize) -> &mut Self {
297 let _ = self.write_side.insert(Arc::new(PlainCache::new(
298 path.as_ref().to_owned(),
299 capacity,
300 )));
301 self
302 }
303
304 pub fn sharded_writer(
308 &mut self,
309 path: impl AsRef<Path>,
310 num_shards: usize,
311 total_capacity: usize,
312 ) -> &mut Self {
313 let _ = self.write_side.insert(Arc::new(ShardedCache::new(
314 path.as_ref().to_owned(),
315 num_shards,
316 total_capacity,
317 )));
318 self
319 }
320
321 pub fn auto_sync(&mut self, sync: bool) -> &mut Self {
337 self.auto_sync = sync;
338 self
339 }
340
341 pub fn reader(&mut self, path: impl AsRef<Path>, num_shards: usize) -> &mut Self {
347 self.read_side.cache(path, num_shards);
348 self
349 }
350
351 pub fn plain_reader(&mut self, path: impl AsRef<Path>) -> &mut Self {
354 self.read_side.plain(path);
355 self
356 }
357
358 pub fn plain_readers(
362 &mut self,
363 paths: impl IntoIterator<Item = impl AsRef<Path>>,
364 ) -> &mut Self {
365 self.read_side.plain_caches(paths);
366 self
367 }
368
369 pub fn sharded_reader(&mut self, path: impl AsRef<Path>, num_shards: usize) -> &mut Self {
372 self.read_side.sharded(path, num_shards);
373 self
374 }
375
376 pub fn take(&mut self) -> Self {
381 std::mem::take(self)
382 }
383
384 pub fn build(self) -> Cache {
387 Cache {
388 write_side: self.write_side,
389 auto_sync: self.auto_sync,
390 consistency_checker: self.consistency_checker,
391 read_side: self.read_side.build(),
392 }
393 }
394}
395
396#[cfg(target_family = "unix")]
402fn fix_tempfile_permissions(file: &NamedTempFile) -> Result<()> {
403 use std::fs::Permissions;
404 use std::os::unix::fs::PermissionsExt;
405
406 file.as_file()
407 .set_permissions(Permissions::from_mode(0o444))
408}
409
410#[cfg(not(target_family = "unix"))]
411fn fix_tempfile_permissions(_: &NamedTempFile) -> Result<()> {
412 Ok(())
413}
414
415impl Cache {
416 #[inline]
419 fn maybe_sync(&self, file: &File) -> Result<()> {
420 if self.auto_sync {
421 file.sync_all()
422 } else {
423 Ok(())
424 }
425 }
426
427 fn maybe_sync_path(&self, path: &Path) -> Result<()> {
437 if self.auto_sync {
438 std::fs::File::open(path)?
442 .sync_all()
443 .expect("auto_sync failed, and failure semantics are unclear for fsync");
444 }
445
446 Ok(())
447 }
448
449 pub fn get<'a>(&self, key: impl Into<Key<'a>>) -> Result<Option<File>> {
465 fn doit(
466 write_side: Option<&dyn FullCache>,
467 checker: Option<&ConsistencyChecker>,
468 read_side: &ReadOnlyCache,
469 key: Key,
470 ) -> Result<Option<File>> {
471 use std::io::Seek;
472 use std::io::SeekFrom;
473
474 if let Some(write) = write_side {
475 if let Some(mut ret) = write.get(key)? {
476 if let Some(checker) = checker {
477 if let Some(mut read_hit) = read_side.get(key)? {
478 checker(&mut ret, &mut read_hit)?;
479 ret.seek(SeekFrom::Start(0))?;
480 }
481 }
482
483 return Ok(Some(ret));
484 }
485 }
486
487 read_side.get(key)
488 }
489
490 doit(
491 self.write_side.as_ref().map(AsRef::as_ref),
492 self.consistency_checker.as_ref(),
493 &self.read_side,
494 key.into(),
495 )
496 }
497
498 pub fn ensure<'a>(
512 &self,
513 key: impl Into<Key<'a>>,
514 populate: impl FnOnce(&mut File) -> Result<()>,
515 ) -> Result<File> {
516 fn judge(_: CacheHit) -> CacheHitAction {
517 CacheHitAction::Promote
518 }
519
520 self.get_or_update(key, judge, |dst, _| populate(dst))
521 }
522
523 pub fn get_or_update<'a>(
551 &self,
552 key: impl Into<Key<'a>>,
553 judge: impl FnOnce(CacheHit) -> CacheHitAction,
554 populate: impl FnOnce(&mut File, Option<File>) -> Result<()>,
555 ) -> Result<File> {
556 use std::io::Seek;
557 use std::io::SeekFrom;
558
559 fn promote(cache: &dyn FullCache, sync: bool, key: Key, mut file: File) -> Result<File> {
561 let mut tmp = NamedTempFile::new_in(cache.temp_dir(key)?)?;
562 std::io::copy(&mut file, tmp.as_file_mut())?;
563 fix_tempfile_permissions(&tmp)?;
564
565 if sync {
569 tmp.as_file().sync_all()?;
570 }
571
572 cache.put(key, tmp.path())?;
573
574 file.seek(SeekFrom::Start(0))?;
576 Ok(file)
577 }
578
579 let cache_or = self.write_side.as_ref().map(Arc::as_ref);
580 let key: Key = key.into();
581
582 let get_tempfile = || {
583 if let Some(cache) = cache_or {
584 tempfile::tempfile_in(cache.temp_dir(key)?)
585 } else {
586 tempfile::tempfile()
587 }
588 };
589
590 let mut old = None; if let Some(mut file) = cache_or
592 .and_then(|cache| cache.get(key).transpose())
593 .transpose()?
594 {
595 if let Some(checker) = self.consistency_checker.as_ref() {
596 if let Some(mut read) = self.read_side.get(key)? {
597 checker(&mut file, &mut read)?;
598 file.seek(SeekFrom::Start(0))?;
599 }
600 }
601
602 match judge(CacheHit::Primary(&mut file)) {
603 CacheHitAction::Accept | CacheHitAction::Promote => {
605 file.seek(SeekFrom::Start(0))?;
606
607 if let Some(checker) = self.consistency_checker.as_ref() {
608 let mut tmp = get_tempfile()?;
609 match populate(&mut tmp, None) {
610 Err(e) if e.kind() == ErrorKind::NotFound => {
611 return Ok(file);
612 }
613 ret => ret?,
614 };
615 tmp.seek(SeekFrom::Start(0))?;
616 checker(&mut file, &mut tmp)?;
617 file.seek(SeekFrom::Start(0))?;
618 }
619
620 return Ok(file);
621 }
622 CacheHitAction::Replace => old = Some(file),
623 }
624 } else if let Some(mut file) = self.read_side.get(key)? {
625 match judge(CacheHit::Secondary(&mut file)) {
626 j @ CacheHitAction::Accept | j @ CacheHitAction::Promote => {
627 file.seek(SeekFrom::Start(0))?;
628
629 if let Some(checker) = self.consistency_checker.as_ref() {
630 let mut tmp = get_tempfile()?;
631
632 match populate(&mut tmp, None) {
633 Err(e) if e.kind() == ErrorKind::NotFound => {
634 return Ok(file);
635 }
636 ret => ret?,
637 };
638
639 tmp.seek(SeekFrom::Start(0))?;
640 checker(&mut file, &mut tmp)?;
641 file.seek(SeekFrom::Start(0))?;
642 }
643
644 return if matches!(j, CacheHitAction::Accept) {
645 Ok(file)
646 } else if let Some(cache) = cache_or {
647 promote(cache, self.auto_sync, key, file)
648 } else {
649 Ok(file)
650 };
651 }
652 CacheHitAction::Replace => old = Some(file),
653 }
654 }
655
656 let cache = match cache_or {
657 Some(cache) => cache,
658 None => {
659 let mut tmp = tempfile::tempfile()?;
662 populate(&mut tmp, old)?;
663
664 tmp.seek(SeekFrom::Start(0))?;
665 return Ok(tmp);
666 }
667 };
668
669 let replace = old.is_some();
670 let mut tmp = NamedTempFile::new_in(cache.temp_dir(key)?)?;
673 populate(tmp.as_file_mut(), old)?;
674 fix_tempfile_permissions(&tmp)?;
675 self.maybe_sync(tmp.as_file())?;
676
677 let path = tmp.path();
679 let mut ret = File::open(path)?;
680 if replace {
681 cache.set(key, path)?;
682 } else {
683 cache.put(key, path)?;
684 if let Ok(Some(file)) = cache.get(key) {
686 ret = file;
687 }
688 }
689
690 Ok(ret)
691 }
692
693 fn set_impl(&self, key: Key, value: &Path) -> Result<()> {
694 match self.write_side.as_ref() {
695 Some(write) => write.set(key, value),
696 None => Err(Error::new(
697 ErrorKind::Unsupported,
698 "no kismet write cache defined",
699 )),
700 }
701 }
702
703 pub fn set<'a>(&self, key: impl Into<Key<'a>>, value: impl AsRef<Path>) -> Result<()> {
727 fn doit(this: &Cache, key: Key, value: &Path) -> Result<()> {
728 this.maybe_sync_path(value)?;
729 this.set_impl(key, value)
730 }
731
732 doit(self, key.into(), value.as_ref())
733 }
734
735 pub fn set_temp_file<'a>(&self, key: impl Into<Key<'a>>, value: NamedTempFile) -> Result<()> {
741 fn doit(this: &Cache, key: Key, value: NamedTempFile) -> Result<()> {
742 fix_tempfile_permissions(&value)?;
743 this.maybe_sync(value.as_file())?;
744 this.set_impl(key, value.path())
745 }
746
747 doit(self, key.into(), value)
748 }
749
750 fn put_impl(&self, key: Key, value: &Path) -> Result<()> {
751 match self.write_side.as_ref() {
752 Some(write) => write.put(key, value),
753 None => Err(Error::new(
754 ErrorKind::Unsupported,
755 "no kismet write cache defined",
756 )),
757 }
758 }
759
760 pub fn put<'a>(&self, key: impl Into<Key<'a>>, value: impl AsRef<Path>) -> Result<()> {
785 fn doit(this: &Cache, key: Key, value: &Path) -> Result<()> {
786 this.maybe_sync_path(value)?;
787 this.put_impl(key, value)
788 }
789
790 doit(self, key.into(), value.as_ref())
791 }
792
793 pub fn put_temp_file<'a>(&self, key: impl Into<Key<'a>>, value: NamedTempFile) -> Result<()> {
799 fn doit(this: &Cache, key: Key, value: NamedTempFile) -> Result<()> {
800 fix_tempfile_permissions(&value)?;
801 this.maybe_sync(value.as_file())?;
802 this.put_impl(key, value.path())
803 }
804
805 doit(self, key.into(), value)
806 }
807
808 pub fn touch<'a>(&self, key: impl Into<Key<'a>>) -> Result<bool> {
821 fn doit(
822 write_side: Option<&dyn FullCache>,
823 read_side: &ReadOnlyCache,
824 key: Key,
825 ) -> Result<bool> {
826 if let Some(write) = write_side {
827 if write.touch(key)? {
828 return Ok(true);
829 }
830 }
831
832 read_side.touch(key)
833 }
834
835 doit(
836 self.write_side.as_ref().map(AsRef::as_ref),
837 &self.read_side,
838 key.into(),
839 )
840 }
841}
842
843#[cfg(test)]
844mod test {
845 use std::fs::File;
846 use std::io::ErrorKind;
847 use std::sync::atomic::AtomicU64;
848 use std::sync::atomic::Ordering;
849 use std::sync::Arc;
850
851 use crate::plain::Cache as PlainCache;
852 use crate::sharded::Cache as ShardedCache;
853 use crate::Cache;
854 use crate::CacheBuilder;
855 use crate::CacheHit;
856 use crate::CacheHitAction;
857 use crate::Key;
858
859 struct TestKey {
860 key: String,
861 }
862
863 impl TestKey {
864 fn new(key: &str) -> TestKey {
865 TestKey {
866 key: key.to_string(),
867 }
868 }
869 }
870
871 impl<'a> From<&'a TestKey> for Key<'a> {
872 fn from(x: &'a TestKey) -> Key<'a> {
873 Key::new(&x.key, 0, 1)
874 }
875 }
876
877 fn byte_equality_checker(
878 counter: Arc<AtomicU64>,
879 ) -> impl 'static + Fn(&mut File, &mut File) -> std::io::Result<()> {
880 move |x: &mut File, y: &mut File| {
881 counter.fetch_add(1, Ordering::Relaxed);
882 crate::byte_equality_checker(x, y)
883 }
884 }
885
886 #[test]
889 fn empty() {
890 use test_dir::{DirBuilder, FileType, TestDir};
891
892 let temp = TestDir::temp().create("foo", FileType::RandomFile(10));
893 let cache: Cache = Default::default();
894
895 assert!(matches!(cache.get(&TestKey::new("foo")), Ok(None)));
896 assert!(matches!(
899 cache.ensure(&TestKey::new("foo"), |_| Ok(())),
900 Ok(_)
901 ));
902
903 assert!(matches!(cache.set(&TestKey::new("foo"), &temp.path("foo")),
904 Err(e) if e.kind() == ErrorKind::Unsupported));
905 assert!(matches!(cache.put(&TestKey::new("foo"), &temp.path("foo")),
906 Err(e) if e.kind() == ErrorKind::Unsupported));
907 assert!(matches!(cache.touch(&TestKey::new("foo")), Ok(false)));
908 }
909
910 #[test]
913 fn empty_no_auto_sync() {
914 let cache = CacheBuilder::new().auto_sync(false).take().build();
915
916 assert!(matches!(cache.get(&TestKey::new("foo")), Ok(None)));
917 assert!(matches!(
918 cache.ensure(&TestKey::new("foo"), |_| Ok(())),
919 Ok(_)
920 ));
921
922 assert!(
923 matches!(cache.set(&TestKey::new("foo"), "/no-such-tmp/foo"),
924 Err(e) if e.kind() == ErrorKind::Unsupported)
925 );
926 assert!(
927 matches!(cache.put(&TestKey::new("foo"), "/no-such-tmp/foo"),
928 Err(e) if e.kind() == ErrorKind::Unsupported)
929 );
930 assert!(matches!(cache.touch(&TestKey::new("foo")), Ok(false)));
931 }
932
933 #[test]
936 fn consistency_checker_success() {
937 use std::io::Error;
938 use std::io::ErrorKind;
939 use std::io::Read;
940 use std::io::Write;
941 use test_dir::{DirBuilder, FileType, TestDir};
942
943 let temp = TestDir::temp()
944 .create("first", FileType::Dir)
945 .create("second", FileType::Dir)
946 .create("first/0", FileType::ZeroFile(2))
947 .create("second/0", FileType::ZeroFile(2))
948 .create("first/1", FileType::ZeroFile(1))
949 .create("second/2", FileType::ZeroFile(3))
950 .create("second/3", FileType::ZeroFile(3))
951 .create("second/4", FileType::ZeroFile(4));
952
953 let counter = Arc::new(AtomicU64::new(0));
954
955 let cache = CacheBuilder::new()
956 .plain_writer(temp.path("first"), 100)
957 .plain_reader(temp.path("second"))
958 .consistency_checker(byte_equality_checker(counter.clone()))
959 .take()
960 .build();
961
962 {
964 let mut hit = cache
965 .get(&TestKey::new("0"))
966 .expect("must succeed")
967 .expect("must exist");
968
969 assert_eq!(counter.load(Ordering::Relaxed), 1);
970
971 let mut contents = Vec::new();
972 hit.read_to_end(&mut contents).expect("read should succeed");
973 assert_eq!(contents, "00".as_bytes());
974 }
975
976 {
978 counter.store(0, Ordering::Relaxed);
979 let mut populated = cache
980 .ensure(&TestKey::new("0"), |dst| {
981 dst.write_all("00".as_bytes())?;
982 Ok(())
983 })
984 .expect("ensure must succeed");
985
986 assert_eq!(counter.load(Ordering::Relaxed), 2);
987
988 let mut contents = Vec::new();
989 populated
990 .read_to_end(&mut contents)
991 .expect("read should succeed");
992 assert_eq!(contents, "00".as_bytes());
993 }
994
995 {
998 counter.store(0, Ordering::Relaxed);
999 let mut populated = cache
1000 .ensure(&TestKey::new("0"), |_| {
1001 Err(Error::new(ErrorKind::NotFound, "not found"))
1002 })
1003 .expect("ensure must succeed");
1004
1005 assert_eq!(counter.load(Ordering::Relaxed), 1);
1006
1007 let mut contents = Vec::new();
1008 populated
1009 .read_to_end(&mut contents)
1010 .expect("read should succeed");
1011 assert_eq!(contents, "00".as_bytes());
1012 }
1013
1014 counter.store(0, Ordering::Relaxed);
1015 let _ = cache
1016 .get(&TestKey::new("1"))
1017 .expect("must succeed")
1018 .expect("must exist");
1019 assert_eq!(counter.load(Ordering::Relaxed), 0);
1021
1022 {
1024 let mut populated = cache
1025 .ensure(&TestKey::new("1"), |dst| {
1026 dst.write_all("0".as_bytes())?;
1027 Ok(())
1028 })
1029 .expect("ensure must succeed");
1030
1031 assert_eq!(counter.load(Ordering::Relaxed), 1);
1032
1033 let mut contents = Vec::new();
1034 populated
1035 .read_to_end(&mut contents)
1036 .expect("read should succeed");
1037 assert_eq!(contents, "0".as_bytes());
1038 }
1039
1040 counter.store(0, Ordering::Relaxed);
1041 let _ = cache
1042 .get(&TestKey::new("2"))
1043 .expect("must succeed")
1044 .expect("must exist");
1045 assert_eq!(counter.load(Ordering::Relaxed), 0);
1047
1048 {
1050 counter.store(0, Ordering::Relaxed);
1051 let mut populated = cache
1052 .ensure(&TestKey::new("2"), |dst| {
1053 dst.write_all("000".as_bytes())?;
1054 Ok(())
1055 })
1056 .expect("ensure must succeed");
1057
1058 assert_eq!(counter.load(Ordering::Relaxed), 1);
1059
1060 let mut contents = Vec::new();
1061 populated
1062 .read_to_end(&mut contents)
1063 .expect("read should succeed");
1064 assert_eq!(contents, "000".as_bytes());
1065 }
1066
1067 {
1068 counter.store(0, Ordering::Relaxed);
1069 let mut populated = cache
1070 .get_or_update(
1071 &TestKey::new("3"),
1072 |_| CacheHitAction::Accept,
1073 |dst, _| {
1074 dst.write_all("000".as_bytes())?;
1075 Ok(())
1076 },
1077 )
1078 .expect("get_or_update must succeed");
1079
1080 assert_eq!(counter.load(Ordering::Relaxed), 1);
1081
1082 let mut contents = Vec::new();
1083 populated
1084 .read_to_end(&mut contents)
1085 .expect("read should succeed");
1086 assert_eq!(contents, "000".as_bytes());
1087 }
1088
1089 {
1091 counter.store(0, Ordering::Relaxed);
1092 let mut populated = cache
1093 .get_or_update(
1094 &TestKey::new("4"),
1095 |_| CacheHitAction::Accept,
1096 |_, _| Err(Error::new(ErrorKind::NotFound, "not found")),
1097 )
1098 .expect("get_or_update must succeed");
1099
1100 assert_eq!(counter.load(Ordering::Relaxed), 0);
1101
1102 let mut contents = Vec::new();
1103 populated
1104 .read_to_end(&mut contents)
1105 .expect("read should succeed");
1106 assert_eq!(contents, "0000".as_bytes());
1107 }
1108
1109 {
1111 counter.store(0, Ordering::Relaxed);
1112 let mut populated = cache
1113 .get_or_update(
1114 &TestKey::new("no-such-key"),
1115 |_| CacheHitAction::Accept,
1116 |dst, _| {
1117 dst.write_all("fresh data".as_bytes())?;
1118 Ok(())
1119 },
1120 )
1121 .expect("get_or_update must succeed");
1122
1123 assert_eq!(counter.load(Ordering::Relaxed), 0);
1124
1125 let mut contents = Vec::new();
1126 populated
1127 .read_to_end(&mut contents)
1128 .expect("read should succeed");
1129 assert_eq!(contents, "fresh data".as_bytes());
1130 }
1131 }
1132
1133 #[test]
1136 fn consistency_checker_failure() {
1137 use std::io::Write;
1138 use test_dir::{DirBuilder, FileType, TestDir};
1139
1140 let temp = TestDir::temp()
1141 .create("first", FileType::Dir)
1142 .create("second", FileType::Dir)
1143 .create("first/0", FileType::ZeroFile(2))
1144 .create("second/0", FileType::ZeroFile(3))
1145 .create("first/1", FileType::ZeroFile(1))
1146 .create("second/2", FileType::ZeroFile(4));
1147
1148 let counter = Arc::new(AtomicU64::new(0));
1149 let cache = CacheBuilder::new()
1150 .plain_writer(temp.path("first"), 100)
1151 .plain_reader(temp.path("second"))
1152 .consistency_checker(byte_equality_checker(counter))
1153 .take()
1154 .build();
1155
1156 assert!(cache.get(&TestKey::new("0")).is_err());
1158
1159 assert!(cache
1161 .ensure(&TestKey::new("0"), |_| {
1162 unreachable!("should detect read-cache mismatch first");
1163 })
1164 .is_err());
1165
1166 assert!(cache
1169 .ensure(&TestKey::new("1"), |dst| {
1170 dst.write_all("0000".as_bytes())?;
1171 Ok(())
1172 })
1173 .is_err());
1174
1175 assert!(cache
1176 .ensure(&TestKey::new("2"), |dst| {
1177 dst.write_all("0".as_bytes())?;
1178 Ok(())
1179 })
1180 .is_err());
1181
1182 assert!(cache
1184 .get_or_update(
1185 &TestKey::new("2"),
1186 |_| CacheHitAction::Accept,
1187 |dst, _| {
1188 dst.write_all("0".as_bytes())?;
1189 Ok(())
1190 }
1191 )
1192 .is_err());
1193 }
1194
1195 #[test]
1198 fn consistency_checker_silent_failure() {
1199 use test_dir::{DirBuilder, FileType, TestDir};
1200
1201 let temp = TestDir::temp()
1202 .create("first", FileType::Dir)
1203 .create("second", FileType::Dir)
1204 .create("first/0", FileType::ZeroFile(2))
1205 .create("second/0", FileType::ZeroFile(3))
1206 .create("first/1", FileType::ZeroFile(1))
1207 .create("second/2", FileType::ZeroFile(4));
1208
1209 let counter = Arc::new(AtomicU64::new(0));
1210
1211 let cache = CacheBuilder::new()
1212 .plain_writer(temp.path("first"), 100)
1213 .plain_reader(temp.path("second"))
1214 .consistency_checker(byte_equality_checker(counter.clone()))
1215 .clear_consistency_checker()
1216 .take()
1217 .build();
1218
1219 let _ = cache
1221 .get(&TestKey::new("0"))
1222 .expect("must succeed")
1223 .expect("must exist");
1224
1225 let _ = cache
1227 .ensure(&TestKey::new("0"), |_| {
1228 unreachable!("should not be called");
1229 })
1230 .expect("must succeed");
1231
1232 let _ = cache
1233 .ensure(&TestKey::new("1"), |_| {
1234 unreachable!("should not be called");
1235 })
1236 .expect("must succeed");
1237
1238 let _ = cache
1239 .ensure(&TestKey::new("2"), |_| {
1240 unreachable!("should not be called");
1241 })
1242 .expect("must succeed");
1243
1244 let _ = cache
1245 .get_or_update(
1246 &TestKey::new("2"),
1247 |_| CacheHitAction::Accept,
1248 |_, _| {
1249 unreachable!("should not be called");
1250 },
1251 )
1252 .expect("must succeed");
1253 assert_eq!(counter.load(Ordering::Relaxed), 0);
1255 }
1256
1257 #[test]
1259 fn two_plain_caches() {
1260 use test_dir::{DirBuilder, FileType, TestDir};
1261
1262 let temp = TestDir::temp()
1263 .create("first", FileType::Dir)
1264 .create("second", FileType::Dir)
1265 .create("first/0", FileType::ZeroFile(2))
1266 .create("second/1", FileType::ZeroFile(3));
1267
1268 let ro = CacheBuilder::new()
1269 .plain_readers(["first", "second"].iter().map(|p| temp.path(p)))
1270 .take()
1271 .build();
1272
1273 let _ = ro
1275 .get(&TestKey::new("0"))
1276 .expect("must succeed")
1277 .expect("must exist");
1278
1279 let _ = ro
1280 .get(&TestKey::new("1"))
1281 .expect("must succeed")
1282 .expect("must exist");
1283
1284 assert!(ro.get(&TestKey::new("2")).expect("must succeed").is_none());
1286 }
1287
1288 #[test]
1290 fn test_ensure() {
1291 use std::io::{Read, Write};
1292 use test_dir::{DirBuilder, TestDir};
1293
1294 let temp = TestDir::temp();
1295 let cache = CacheBuilder::new()
1297 .writer(temp.path("."), 1, 10)
1298 .auto_sync(false)
1299 .take()
1300 .build();
1301 let key = TestKey::new("foo");
1302
1303 assert!(matches!(cache.get(&key), Ok(None)));
1305
1306 {
1307 let mut populated = cache
1308 .ensure(&key, |file| file.write_all(b"test"))
1309 .expect("ensure must succeed");
1310
1311 let mut dst = Vec::new();
1312 populated.read_to_end(&mut dst).expect("read must succeed");
1313 assert_eq!(&dst, b"test");
1314 }
1315
1316 {
1318 let mut fetched = cache
1319 .get(&key)
1320 .expect("get must succeed")
1321 .expect("file must be found");
1322
1323 let mut dst = Vec::new();
1324 fetched.read_to_end(&mut dst).expect("read must succeed");
1325 assert_eq!(&dst, b"test");
1326 }
1327
1328 {
1330 let mut populated = cache
1331 .ensure(&key, |_| {
1332 unreachable!("should not be called for an extant file")
1333 })
1334 .expect("ensure must succeed");
1335
1336 let mut dst = Vec::new();
1337 populated.read_to_end(&mut dst).expect("read must succeed");
1338 assert_eq!(&dst, b"test");
1339 }
1340 }
1341
1342 #[test]
1345 fn test_ensure_promote() {
1346 use std::io::{Read, Write};
1347 use tempfile::NamedTempFile;
1348 use test_dir::{DirBuilder, FileType, TestDir};
1349
1350 let temp = TestDir::temp()
1351 .create("cache", FileType::Dir)
1352 .create("extra_plain", FileType::Dir);
1353
1354 {
1356 let cache = PlainCache::new(temp.path("extra_plain"), 10);
1357
1358 let tmp = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1359 .expect("new temp file must succeed");
1360 tmp.as_file()
1361 .write_all(b"initial")
1362 .expect("write must succeed");
1363
1364 cache.put("foo", tmp.path()).expect("put must succeed");
1365 }
1366
1367 let cache = CacheBuilder::new()
1368 .writer(temp.path("cache"), 1, 10)
1369 .plain_reader(temp.path("extra_plain"))
1370 .take()
1371 .build();
1372 let key = TestKey::new("foo");
1373
1374 {
1376 let mut fetched = cache
1377 .get(&key)
1378 .expect("get must succeed")
1379 .expect("file must be found");
1380
1381 let mut dst = Vec::new();
1382 fetched.read_to_end(&mut dst).expect("read must succeed");
1383 assert_eq!(&dst, b"initial");
1384 }
1385
1386 {
1387 let mut populated = cache
1388 .ensure(&key, |_| {
1389 unreachable!("should not be called for an extant file")
1390 })
1391 .expect("ensure must succeed");
1392
1393 let mut dst = Vec::new();
1394 populated.read_to_end(&mut dst).expect("read must succeed");
1395 assert_eq!(&dst, b"initial");
1396 }
1397
1398 {
1401 let new_cache = CacheBuilder::new()
1402 .writer(temp.path("cache"), 1, 10)
1403 .take()
1404 .build();
1405 let mut fetched = new_cache
1406 .get(&key)
1407 .expect("get must succeed")
1408 .expect("file must be found");
1409
1410 let mut dst = Vec::new();
1411 fetched.read_to_end(&mut dst).expect("read must succeed");
1412 assert_eq!(&dst, b"initial");
1413 }
1414 }
1415
1416 #[test]
1419 fn test_get_or_update_accept() {
1420 use std::io::{Read, Write};
1421 use tempfile::NamedTempFile;
1422 use test_dir::{DirBuilder, FileType, TestDir};
1423
1424 let temp = TestDir::temp()
1425 .create("cache", FileType::Dir)
1426 .create("extra_plain", FileType::Dir);
1427
1428 {
1430 let cache = PlainCache::new(temp.path("extra_plain"), 10);
1431
1432 let tmp = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1433 .expect("new temp file must succeed");
1434 tmp.as_file()
1435 .write_all(b"initial")
1436 .expect("write must succeed");
1437
1438 cache.put("foo", tmp.path()).expect("put must succeed");
1439 }
1440
1441 let cache = CacheBuilder::new()
1442 .writer(temp.path("cache"), 2, 10)
1444 .plain_reader(temp.path("extra_plain"))
1445 .take()
1446 .build();
1447 let key = TestKey::new("foo");
1448 let key2 = TestKey::new("bar");
1449
1450 {
1452 let mut fetched = cache
1453 .get_or_update(
1454 &key,
1455 |hit| {
1456 assert!(matches!(hit, CacheHit::Secondary(_)));
1457 CacheHitAction::Accept
1458 },
1459 |_, _| unreachable!("should not have to fill an extant file"),
1460 )
1461 .expect("get_or_update must succeed");
1462
1463 let mut dst = Vec::new();
1464 fetched.read_to_end(&mut dst).expect("read must succeed");
1465 assert_eq!(&dst, b"initial");
1466 }
1467
1468 {
1470 let mut fetched = cache
1471 .get_or_update(
1472 &key2,
1473 |_| unreachable!("should not be called"),
1474 |file, old| {
1475 assert!(old.is_none());
1476 file.write_all(b"updated")
1477 },
1478 )
1479 .expect("get_or_update must succeed");
1480
1481 let mut dst = Vec::new();
1482 fetched.read_to_end(&mut dst).expect("read must succeed");
1483 assert_eq!(&dst, b"updated");
1484 }
1485
1486 {
1488 let mut fetched = cache
1489 .get_or_update(
1490 &key2,
1491 |hit| {
1492 assert!(matches!(hit, CacheHit::Primary(_)));
1493 CacheHitAction::Accept
1494 },
1495 |_, _| unreachable!("should not have to fill an extant file"),
1496 )
1497 .expect("get_or_update must succeed");
1498
1499 let mut dst = Vec::new();
1500 fetched.read_to_end(&mut dst).expect("read must succeed");
1501 assert_eq!(&dst, b"updated");
1502 }
1503
1504 {
1507 let new_cache = CacheBuilder::new()
1508 .writer(temp.path("cache"), 2, 10)
1509 .take()
1510 .build();
1511
1512 assert!(matches!(new_cache.touch(&key), Ok(false)));
1514
1515 let mut fetched = new_cache
1517 .get(&key2)
1518 .expect("get must succeed")
1519 .expect("file must be found");
1520
1521 let mut dst = Vec::new();
1522 fetched.read_to_end(&mut dst).expect("read must succeed");
1523 assert_eq!(&dst, b"updated");
1524 }
1525 }
1526
1527 #[test]
1530 fn test_get_or_update_replace() {
1531 use std::io::{Read, Write};
1532 use tempfile::NamedTempFile;
1533 use test_dir::{DirBuilder, FileType, TestDir};
1534
1535 let temp = TestDir::temp()
1536 .create("cache", FileType::Dir)
1537 .create("extra_plain", FileType::Dir);
1538
1539 {
1541 let cache = PlainCache::new(temp.path("extra_plain"), 10);
1542
1543 let tmp = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1544 .expect("new temp file must succeed");
1545 tmp.as_file()
1546 .write_all(b"initial")
1547 .expect("write must succeed");
1548
1549 cache.put("foo", tmp.path()).expect("put must succeed");
1550 }
1551
1552 let cache = CacheBuilder::new()
1553 .writer(temp.path("cache"), 2, 10)
1555 .plain_reader(temp.path("extra_plain"))
1556 .take()
1557 .build();
1558 let key = TestKey::new("foo");
1559
1560 {
1561 let mut fetched = cache
1562 .get_or_update(
1563 &key,
1564 |hit| {
1565 assert!(matches!(hit, CacheHit::Secondary(_)));
1566 CacheHitAction::Replace
1567 },
1568 |file, old| {
1569 let mut prev = old.expect("must have old data");
1571 let mut dst = Vec::new();
1572 prev.read_to_end(&mut dst).expect("read must succeed");
1573 assert_eq!(&dst, b"initial");
1574
1575 file.write_all(b"replace1")
1576 },
1577 )
1578 .expect("get_or_update must succeed");
1579
1580 let mut dst = Vec::new();
1581 fetched.read_to_end(&mut dst).expect("read must succeed");
1582 assert_eq!(&dst, b"replace1");
1583 }
1584
1585 {
1587 let mut fetched = cache
1588 .get(&key)
1589 .expect("get must succeed")
1590 .expect("file should be found");
1591
1592 let mut dst = Vec::new();
1593 fetched.read_to_end(&mut dst).expect("read must succeed");
1594 assert_eq!(&dst, b"replace1");
1595 }
1596
1597 {
1599 let mut fetched = cache
1600 .get_or_update(
1601 &key,
1602 |hit| {
1603 assert!(matches!(hit, CacheHit::Primary(_)));
1604 CacheHitAction::Replace
1605 },
1606 |file, old| {
1607 let mut prev = old.expect("must have old data");
1609 let mut dst = Vec::new();
1610 prev.read_to_end(&mut dst).expect("read must succeed");
1611 assert_eq!(&dst, b"replace1");
1612
1613 file.write_all(b"replace2")
1614 },
1615 )
1616 .expect("get_or_update must succeed");
1617
1618 let mut dst = Vec::new();
1619 fetched.read_to_end(&mut dst).expect("read must succeed");
1620 assert_eq!(&dst, b"replace2");
1621 }
1622
1623 {
1625 let mut fetched = cache
1626 .get_or_update(
1627 &key,
1628 |hit| {
1629 assert!(matches!(hit, CacheHit::Primary(_)));
1630 CacheHitAction::Replace
1631 },
1632 |file, old| {
1633 let mut prev = old.expect("must have old data");
1635 let mut dst = Vec::new();
1636 prev.read_to_end(&mut dst).expect("read must succeed");
1637 assert_eq!(&dst, b"replace2");
1638
1639 file.write_all(b"replace3")
1640 },
1641 )
1642 .expect("get_or_update must succeed");
1643
1644 let mut dst = Vec::new();
1645 fetched.read_to_end(&mut dst).expect("read must succeed");
1646 assert_eq!(&dst, b"replace3");
1647 }
1648
1649 {
1652 let new_cache = CacheBuilder::new()
1653 .writer(temp.path("cache"), 2, 10)
1654 .take()
1655 .build();
1656
1657 let mut fetched = new_cache
1659 .get(&key)
1660 .expect("get must succeed")
1661 .expect("file must be found");
1662
1663 let mut dst = Vec::new();
1664 fetched.read_to_end(&mut dst).expect("read must succeed");
1665 assert_eq!(&dst, b"replace3");
1666 }
1667 }
1668
1669 #[test]
1673 fn test_ensure_no_write_side() {
1674 use std::io::{Read, Write};
1675 use tempfile::NamedTempFile;
1676 use test_dir::{DirBuilder, FileType, TestDir};
1677
1678 let temp = TestDir::temp().create("extra_plain", FileType::Dir);
1679
1680 {
1682 let cache = PlainCache::new(temp.path("extra_plain"), 10);
1683
1684 let tmp = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1685 .expect("new temp file must succeed");
1686 tmp.as_file()
1687 .write_all(b"initial")
1688 .expect("write must succeed");
1689
1690 cache.put("foo", tmp.path()).expect("put must succeed");
1691 }
1692
1693 let cache = CacheBuilder::new()
1694 .plain_reader(temp.path("extra_plain"))
1695 .take()
1696 .build();
1697 let key = TestKey::new("foo");
1698 let key2 = TestKey::new("bar");
1699
1700 {
1702 let mut fetched = cache
1703 .get_or_update(
1704 &key,
1705 |hit| {
1706 assert!(matches!(hit, CacheHit::Secondary(_)));
1707 CacheHitAction::Accept
1708 },
1709 |_, _| unreachable!("should not have to fill an extant file"),
1710 )
1711 .expect("get_or_update must succeed");
1712
1713 let mut dst = Vec::new();
1714 fetched.read_to_end(&mut dst).expect("read must succeed");
1715 assert_eq!(&dst, b"initial");
1716 }
1717
1718 {
1720 let mut fetched = cache
1721 .get_or_update(
1722 &key2,
1723 |_| unreachable!("should not be called"),
1724 |file, old| {
1725 assert!(old.is_none());
1726 file.write_all(b"updated")
1727 },
1728 )
1729 .expect("get_or_update must succeed");
1730
1731 let mut dst = Vec::new();
1732 fetched.read_to_end(&mut dst).expect("read must succeed");
1733 assert_eq!(&dst, b"updated");
1734 }
1735
1736 {
1738 let mut fetched = cache
1739 .get_or_update(
1740 &key2,
1741 |_| unreachable!("should not be called"),
1742 |file, old| {
1743 assert!(old.is_none());
1744 file.write_all(b"updated2")
1745 },
1746 )
1747 .expect("get_or_update must succeed");
1748
1749 let mut dst = Vec::new();
1750 fetched.read_to_end(&mut dst).expect("read must succeed");
1751 assert_eq!(&dst, b"updated2");
1752 }
1753 }
1754
1755 #[test]
1758 fn test_byte_equality_checker() {
1759 use test_dir::{DirBuilder, FileType, TestDir};
1760
1761 let temp = TestDir::temp()
1762 .create("first", FileType::Dir)
1763 .create("second", FileType::Dir)
1764 .create("first/0", FileType::ZeroFile(2))
1765 .create("second/0", FileType::ZeroFile(3));
1766
1767 let cache = CacheBuilder::new()
1768 .plain_readers(["first", "second"].iter().map(|p| temp.path(p)))
1769 .byte_equality_checker()
1770 .take()
1771 .build();
1772
1773 assert!(cache.get(&TestKey::new("0")).is_err());
1774 }
1775
1776 #[test]
1779 #[should_panic(expected = "file contents do not match")]
1780 fn test_panicking_byte_equality_checker() {
1781 use test_dir::{DirBuilder, FileType, TestDir};
1782
1783 let temp = TestDir::temp()
1784 .create("first", FileType::Dir)
1785 .create("second", FileType::Dir)
1786 .create("first/0", FileType::ZeroFile(2))
1787 .create("second/0", FileType::ZeroFile(3));
1788
1789 let cache = CacheBuilder::new()
1790 .plain_readers(["first", "second"].iter().map(|p| temp.path(p)))
1791 .panicking_byte_equality_checker()
1792 .take()
1793 .build();
1794
1795 assert!(cache.get(&TestKey::new("0")).is_ok());
1797 }
1798
1799 #[test]
1801 fn smoke_test_plain() {
1802 use std::io::{Read, Write};
1803 use tempfile::NamedTempFile;
1804 use test_dir::{DirBuilder, FileType, TestDir};
1805
1806 let temp = TestDir::temp()
1807 .create("cache", FileType::Dir)
1808 .create("extra", FileType::Dir);
1809
1810 {
1812 let cache = PlainCache::new(temp.path("extra"), 10);
1813
1814 let tmp = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1815 .expect("new temp file must succeed");
1816 tmp.as_file()
1817 .write_all(b"extra")
1818 .expect("write must succeed");
1819
1820 cache.put("b", tmp.path()).expect("put must succeed");
1821
1822 let tmp2 = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1823 .expect("new temp file must succeed");
1824 tmp2.as_file()
1825 .write_all(b"extra2")
1826 .expect("write must succeed");
1827
1828 cache.put("c", tmp2.path()).expect("put must succeed");
1829 }
1830
1831 let cache = CacheBuilder::new()
1832 .writer(temp.path("cache"), 1, 10)
1833 .reader(temp.path("extra"), 1)
1834 .take()
1835 .build();
1836
1837 assert!(matches!(cache.get(&TestKey::new("a")), Ok(None)));
1839 assert!(matches!(cache.touch(&TestKey::new("a")), Ok(false)));
1840
1841 assert!(matches!(cache.touch(&TestKey::new("b")), Ok(true)));
1843
1844 {
1846 let mut b_file = cache
1847 .get(&TestKey::new("b"))
1848 .expect("must succeed")
1849 .expect("must exist");
1850 let mut dst = Vec::new();
1851 b_file.read_to_end(&mut dst).expect("read must succeed");
1852 assert_eq!(&dst, b"extra");
1853 }
1854
1855 {
1857 let tmp = NamedTempFile::new_in(temp.path(".")).expect("new temp file must succeed");
1858
1859 tmp.as_file()
1860 .write_all(b"write")
1861 .expect("write must succeed");
1862 cache
1863 .put(&TestKey::new("a"), tmp.path())
1864 .expect("put must succeed");
1865 }
1866
1867 {
1868 let tmp = NamedTempFile::new_in(temp.path(".")).expect("new temp file must succeed");
1869
1870 tmp.as_file()
1871 .write_all(b"write2")
1872 .expect("write must succeed");
1873 cache
1875 .put_temp_file(&TestKey::new("b"), tmp)
1876 .expect("put must succeed");
1877 }
1878
1879 {
1881 let tmp = NamedTempFile::new_in(temp.path(".")).expect("new temp file must succeed");
1882
1883 tmp.as_file()
1884 .write_all(b"write3")
1885 .expect("write must succeed");
1886 cache
1887 .set(&TestKey::new("a"), tmp.path())
1888 .expect("set must succeed");
1889 }
1890
1891 assert!(matches!(cache.touch(&TestKey::new("a")), Ok(true)));
1898 assert!(matches!(cache.touch(&TestKey::new("b")), Ok(true)));
1899 assert!(matches!(cache.touch(&TestKey::new("c")), Ok(true)));
1900
1901 {
1903 let mut a_file = cache
1904 .get(&TestKey::new("a"))
1905 .expect("must succeed")
1906 .expect("must exist");
1907 let mut dst = Vec::new();
1908 a_file.read_to_end(&mut dst).expect("read must succeed");
1909 assert_eq!(&dst, b"write3");
1910 }
1911
1912 {
1913 let mut b_file = cache
1914 .get(&TestKey::new("b"))
1915 .expect("must succeed")
1916 .expect("must exist");
1917 let mut dst = Vec::new();
1918 b_file.read_to_end(&mut dst).expect("read must succeed");
1919 assert_eq!(&dst, b"write2");
1920 }
1921
1922 {
1923 let mut c_file = cache
1924 .get(&TestKey::new("c"))
1925 .expect("must succeed")
1926 .expect("must exist");
1927 let mut dst = Vec::new();
1928 c_file.read_to_end(&mut dst).expect("read must succeed");
1929 assert_eq!(&dst, b"extra2");
1930 }
1931 }
1932
1933 #[test]
1935 fn smoke_test_sharded() {
1936 use std::io::{Read, Write};
1937 use tempfile::NamedTempFile;
1938 use test_dir::{DirBuilder, FileType, TestDir};
1939
1940 let temp = TestDir::temp()
1941 .create("cache", FileType::Dir)
1942 .create("extra_plain", FileType::Dir)
1943 .create("extra_sharded", FileType::Dir);
1944
1945 {
1947 let cache = PlainCache::new(temp.path("extra_plain"), 10);
1948
1949 let tmp = NamedTempFile::new_in(cache.temp_dir().expect("temp_dir must succeed"))
1950 .expect("new temp file must succeed");
1951 tmp.as_file()
1952 .write_all(b"extra_plain")
1953 .expect("write must succeed");
1954
1955 cache.put("b", tmp.path()).expect("put must succeed");
1956 }
1957
1958 {
1960 let cache = ShardedCache::new(temp.path("extra_sharded"), 10, 10);
1961
1962 let tmp = NamedTempFile::new_in(cache.temp_dir(None).expect("temp_dir must succeed"))
1963 .expect("new temp file must succeed");
1964 tmp.as_file()
1965 .write_all(b"extra_sharded")
1966 .expect("write must succeed");
1967
1968 cache
1969 .put((&TestKey::new("c")).into(), tmp.path())
1970 .expect("put must succeed");
1971 }
1972
1973 let cache = CacheBuilder::new()
1974 .plain_writer(temp.path("cache"), 10)
1975 .writer(temp.path("cache"), 10, 10)
1977 .plain_reader(temp.path("extra_plain"))
1978 .sharded_reader(temp.path("extra_sharded"), 10)
1979 .take()
1980 .build();
1981
1982 assert!(matches!(cache.get(&TestKey::new("a")), Ok(None)));
1984 assert!(matches!(cache.touch(&TestKey::new("a")), Ok(false)));
1985
1986 assert!(matches!(cache.touch(&TestKey::new("b")), Ok(true)));
1988
1989 {
1991 let mut b_file = cache
1992 .get(&TestKey::new("b"))
1993 .expect("must succeed")
1994 .expect("must exist");
1995 let mut dst = Vec::new();
1996 b_file.read_to_end(&mut dst).expect("read must succeed");
1997 assert_eq!(&dst, b"extra_plain");
1998 }
1999
2000 {
2002 let mut c_file = cache
2003 .get(&TestKey::new("c"))
2004 .expect("must succeed")
2005 .expect("must exist");
2006 let mut dst = Vec::new();
2007 c_file.read_to_end(&mut dst).expect("read must succeed");
2008 assert_eq!(&dst, b"extra_sharded");
2009 }
2010
2011 {
2013 let tmp = NamedTempFile::new_in(temp.path(".")).expect("new temp file must succeed");
2014
2015 tmp.as_file()
2016 .write_all(b"write")
2017 .expect("write must succeed");
2018 cache
2019 .set(&TestKey::new("a"), tmp.path())
2020 .expect("set must succeed");
2021 }
2022
2023 {
2024 let tmp = NamedTempFile::new_in(temp.path(".")).expect("new temp file must succeed");
2025
2026 tmp.as_file()
2027 .write_all(b"write2")
2028 .expect("write must succeed");
2029 cache
2031 .set_temp_file(&TestKey::new("b"), tmp)
2032 .expect("set must succeed");
2033 }
2034
2035 {
2037 let tmp = NamedTempFile::new_in(temp.path(".")).expect("new temp file must succeed");
2038
2039 tmp.as_file()
2040 .write_all(b"write3")
2041 .expect("write must succeed");
2042 cache
2043 .put(&TestKey::new("a"), tmp.path())
2044 .expect("put must succeed");
2045 }
2046
2047 assert!(matches!(cache.touch(&TestKey::new("a")), Ok(true)));
2054 assert!(matches!(cache.touch(&TestKey::new("b")), Ok(true)));
2055 assert!(matches!(cache.touch(&TestKey::new("c")), Ok(true)));
2056
2057 {
2059 let mut a_file = cache
2060 .get(&TestKey::new("a"))
2061 .expect("must succeed")
2062 .expect("must exist");
2063 let mut dst = Vec::new();
2064 a_file.read_to_end(&mut dst).expect("read must succeed");
2065 assert_eq!(&dst, b"write");
2066 }
2067
2068 {
2069 let mut b_file = cache
2070 .get(&TestKey::new("b"))
2071 .expect("must succeed")
2072 .expect("must exist");
2073 let mut dst = Vec::new();
2074 b_file.read_to_end(&mut dst).expect("read must succeed");
2075 assert_eq!(&dst, b"write2");
2076 }
2077
2078 {
2079 let mut c_file = cache
2080 .get(&TestKey::new("c"))
2081 .expect("must succeed")
2082 .expect("must exist");
2083 let mut dst = Vec::new();
2084 c_file.read_to_end(&mut dst).expect("read must succeed");
2085 assert_eq!(&dst, b"extra_sharded");
2086 }
2087 }
2088}