]>
Commit | Line | Data |
---|---|---|
529de6c7 DM |
1 | use failure::*; |
2 | ||
3d5c11e5 | 3 | use std::path::{PathBuf, Path}; |
2c32fdde DM |
4 | use std::collections::HashMap; |
5 | use lazy_static::lazy_static; | |
6 | use std::sync::{Mutex, Arc}; | |
529de6c7 DM |
7 | |
8 | use crate::config::datastore; | |
9 | use super::chunk_store::*; | |
10 | use super::image_index::*; | |
0433db19 | 11 | use super::archive_index::*; |
529de6c7 DM |
12 | |
13 | pub struct DataStore { | |
14 | chunk_store: ChunkStore, | |
64e53b28 | 15 | gc_mutex: Mutex<bool>, |
529de6c7 DM |
16 | } |
17 | ||
2c32fdde DM |
18 | lazy_static!{ |
19 | static ref datastore_map: Mutex<HashMap<String, Arc<DataStore>>> = Mutex::new(HashMap::new()); | |
20 | } | |
21 | ||
529de6c7 DM |
22 | impl DataStore { |
23 | ||
2c32fdde DM |
24 | pub fn lookup_datastore(name: &str) -> Result<Arc<DataStore>, Error> { |
25 | ||
26 | let config = datastore::config()?; | |
27 | let (_, store_config) = config.sections.get(name) | |
28 | .ok_or(format_err!("no such datastore '{}'", name))?; | |
29 | ||
30 | let path = store_config["path"].as_str().unwrap(); | |
31 | ||
32 | let mut map = datastore_map.lock().unwrap(); | |
33 | ||
34 | if let Some(datastore) = map.get(name) { | |
35 | // Compare Config - if changed, create new Datastore object! | |
a198d74f | 36 | if datastore.chunk_store.base == PathBuf::from(path) { |
2c32fdde DM |
37 | return Ok(datastore.clone()); |
38 | } | |
39 | } | |
40 | ||
41 | if let Ok(datastore) = DataStore::open(name) { | |
42 | let datastore = Arc::new(datastore); | |
43 | map.insert(name.to_string(), datastore.clone()); | |
44 | return Ok(datastore); | |
45 | } | |
46 | ||
47 | bail!("store not found"); | |
48 | } | |
49 | ||
529de6c7 DM |
50 | pub fn open(store_name: &str) -> Result<Self, Error> { |
51 | ||
52 | let config = datastore::config()?; | |
53 | let (_, store_config) = config.sections.get(store_name) | |
54 | .ok_or(format_err!("no such datastore '{}'", store_name))?; | |
55 | ||
56 | let path = store_config["path"].as_str().unwrap(); | |
57 | ||
277fc5a3 | 58 | let chunk_store = ChunkStore::open(store_name, path)?; |
529de6c7 DM |
59 | |
60 | Ok(Self { | |
61 | chunk_store: chunk_store, | |
64e53b28 | 62 | gc_mutex: Mutex::new(false), |
529de6c7 DM |
63 | }) |
64 | } | |
65 | ||
bcd879cf | 66 | pub fn create_image_writer<P: AsRef<Path>>(&self, filename: P, size: usize, chunk_size: usize) -> Result<ImageIndexWriter, Error> { |
529de6c7 | 67 | |
bcd879cf | 68 | let index = ImageIndexWriter::create(&self.chunk_store, filename.as_ref(), size, chunk_size)?; |
529de6c7 DM |
69 | |
70 | Ok(index) | |
71 | } | |
72 | ||
03e4753d | 73 | pub fn open_image_reader<P: AsRef<Path>>(&self, filename: P) -> Result<ImageIndexReader, Error> { |
529de6c7 | 74 | |
03e4753d | 75 | let index = ImageIndexReader::open(&self.chunk_store, filename.as_ref())?; |
529de6c7 DM |
76 | |
77 | Ok(index) | |
78 | } | |
3d5c11e5 | 79 | |
0433db19 DM |
80 | pub fn create_archive_writer<P: AsRef<Path>>( |
81 | &self, filename: P, | |
82 | chunk_size: usize | |
83 | ) -> Result<ArchiveIndexWriter, Error> { | |
84 | ||
85 | let index = ArchiveIndexWriter::create(&self.chunk_store, filename.as_ref(), chunk_size)?; | |
86 | ||
87 | Ok(index) | |
88 | } | |
89 | ||
77703d95 DM |
90 | pub fn open_archive_reader<P: AsRef<Path>>(&self, filename: P) -> Result<ArchiveIndexReader, Error> { |
91 | ||
92 | let index = ArchiveIndexReader::open(&self.chunk_store, filename.as_ref())?; | |
93 | ||
94 | Ok(index) | |
95 | } | |
96 | ||
3d5c11e5 DM |
97 | pub fn list_images(&self) -> Result<Vec<PathBuf>, Error> { |
98 | let base = self.chunk_store.base_path(); | |
99 | ||
100 | let mut list = vec![]; | |
101 | ||
102 | for entry in std::fs::read_dir(base)? { | |
103 | let entry = entry?; | |
104 | if entry.file_type()?.is_file() { | |
105 | let path = entry.path(); | |
106 | if let Some(ext) = path.extension() { | |
64e53b28 | 107 | if ext == "iidx" { |
3d5c11e5 | 108 | list.push(path); |
77703d95 DM |
109 | } else if ext == "aidx" { |
110 | list.push(path); | |
3d5c11e5 DM |
111 | } |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | Ok(list) | |
117 | } | |
118 | ||
64e53b28 | 119 | fn mark_used_chunks(&self, status: &mut GarbageCollectionStatus) -> Result<(), Error> { |
3d5c11e5 DM |
120 | |
121 | let image_list = self.list_images()?; | |
122 | ||
123 | for path in image_list { | |
77703d95 DM |
124 | if let Some(ext) = path.extension() { |
125 | if ext == "iidx" { | |
bc616633 | 126 | let index = self.open_image_reader(&path)?; |
77703d95 DM |
127 | index.mark_used_chunks(status)?; |
128 | } else if ext == "aidx" { | |
bc616633 | 129 | let index = self.open_archive_reader(&path)?; |
77703d95 DM |
130 | index.mark_used_chunks(status)?; |
131 | } | |
132 | } | |
3d5c11e5 DM |
133 | } |
134 | ||
135 | Ok(()) | |
136 | } | |
137 | ||
03e4753d | 138 | pub fn garbage_collection(&self) -> Result<(), Error> { |
3d5c11e5 | 139 | |
a198d74f | 140 | if let Ok(ref mut _mutex) = self.gc_mutex.try_lock() { |
e95950e4 | 141 | |
64e53b28 DM |
142 | let mut gc_status = GarbageCollectionStatus::default(); |
143 | gc_status.used_bytes = 0; | |
6ea3a0b7 | 144 | |
64e53b28 DM |
145 | println!("Start GC phase1 (mark chunks)"); |
146 | ||
147 | self.mark_used_chunks(&mut gc_status)?; | |
148 | ||
149 | println!("Start GC phase2 (sweep unused chunks)"); | |
77703d95 | 150 | self.chunk_store.sweep_unused_chunks(&mut gc_status)?; |
64e53b28 DM |
151 | |
152 | println!("Used bytes: {}", gc_status.used_bytes); | |
153 | println!("Used chunks: {}", gc_status.used_chunks); | |
154 | println!("Disk bytes: {}", gc_status.disk_bytes); | |
155 | println!("Disk chunks: {}", gc_status.disk_chunks); | |
156 | ||
157 | } else { | |
158 | println!("Start GC failed - (already running/locked)"); | |
159 | } | |
3d5c11e5 DM |
160 | |
161 | Ok(()) | |
162 | } | |
529de6c7 | 163 | } |