]>
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 | 12 | |
ff3d3100 DM |
13 | use chrono::{Utc, TimeZone}; |
14 | ||
529de6c7 | 15 | pub struct DataStore { |
1629d2ad | 16 | chunk_store: Arc<ChunkStore>, |
64e53b28 | 17 | gc_mutex: Mutex<bool>, |
529de6c7 DM |
18 | } |
19 | ||
2c32fdde DM |
20 | lazy_static!{ |
21 | static ref datastore_map: Mutex<HashMap<String, Arc<DataStore>>> = Mutex::new(HashMap::new()); | |
22 | } | |
23 | ||
529de6c7 DM |
24 | impl DataStore { |
25 | ||
2c32fdde DM |
26 | pub fn lookup_datastore(name: &str) -> Result<Arc<DataStore>, Error> { |
27 | ||
28 | let config = datastore::config()?; | |
29 | let (_, store_config) = config.sections.get(name) | |
30 | .ok_or(format_err!("no such datastore '{}'", name))?; | |
31 | ||
32 | let path = store_config["path"].as_str().unwrap(); | |
33 | ||
34 | let mut map = datastore_map.lock().unwrap(); | |
35 | ||
36 | if let Some(datastore) = map.get(name) { | |
37 | // Compare Config - if changed, create new Datastore object! | |
a198d74f | 38 | if datastore.chunk_store.base == PathBuf::from(path) { |
2c32fdde DM |
39 | return Ok(datastore.clone()); |
40 | } | |
41 | } | |
42 | ||
43 | if let Ok(datastore) = DataStore::open(name) { | |
44 | let datastore = Arc::new(datastore); | |
45 | map.insert(name.to_string(), datastore.clone()); | |
46 | return Ok(datastore); | |
47 | } | |
48 | ||
49 | bail!("store not found"); | |
50 | } | |
51 | ||
529de6c7 DM |
52 | pub fn open(store_name: &str) -> Result<Self, Error> { |
53 | ||
54 | let config = datastore::config()?; | |
55 | let (_, store_config) = config.sections.get(store_name) | |
56 | .ok_or(format_err!("no such datastore '{}'", store_name))?; | |
57 | ||
58 | let path = store_config["path"].as_str().unwrap(); | |
59 | ||
277fc5a3 | 60 | let chunk_store = ChunkStore::open(store_name, path)?; |
529de6c7 DM |
61 | |
62 | Ok(Self { | |
1629d2ad | 63 | chunk_store: Arc::new(chunk_store), |
64e53b28 | 64 | gc_mutex: Mutex::new(false), |
529de6c7 DM |
65 | }) |
66 | } | |
67 | ||
bcd879cf | 68 | pub fn create_image_writer<P: AsRef<Path>>(&self, filename: P, size: usize, chunk_size: usize) -> Result<ImageIndexWriter, Error> { |
529de6c7 | 69 | |
150f1bd8 | 70 | let index = ImageIndexWriter::create(self.chunk_store.clone(), filename.as_ref(), size, chunk_size)?; |
529de6c7 DM |
71 | |
72 | Ok(index) | |
73 | } | |
74 | ||
03e4753d | 75 | pub fn open_image_reader<P: AsRef<Path>>(&self, filename: P) -> Result<ImageIndexReader, Error> { |
529de6c7 | 76 | |
150f1bd8 | 77 | let index = ImageIndexReader::open(self.chunk_store.clone(), filename.as_ref())?; |
529de6c7 DM |
78 | |
79 | Ok(index) | |
80 | } | |
3d5c11e5 | 81 | |
0433db19 DM |
82 | pub fn create_archive_writer<P: AsRef<Path>>( |
83 | &self, filename: P, | |
84 | chunk_size: usize | |
85 | ) -> Result<ArchiveIndexWriter, Error> { | |
86 | ||
1629d2ad DM |
87 | let index = ArchiveIndexWriter::create( |
88 | self.chunk_store.clone(), filename.as_ref(), chunk_size)?; | |
0433db19 DM |
89 | |
90 | Ok(index) | |
91 | } | |
ff3d3100 | 92 | |
77703d95 DM |
93 | pub fn open_archive_reader<P: AsRef<Path>>(&self, filename: P) -> Result<ArchiveIndexReader, Error> { |
94 | ||
150f1bd8 | 95 | let index = ArchiveIndexReader::open(self.chunk_store.clone(), filename.as_ref())?; |
77703d95 DM |
96 | |
97 | Ok(index) | |
98 | } | |
99 | ||
ff3d3100 DM |
100 | pub fn base_path(&self) -> PathBuf { |
101 | self.chunk_store.base_path() | |
102 | } | |
103 | ||
104 | pub fn create_backup_dir( | |
105 | &self, | |
106 | backup_type: &str, | |
107 | backup_id: &str, | |
108 | backup_time: i64, | |
109 | ) -> Result<PathBuf, Error> { | |
110 | let mut relative_path = PathBuf::new(); | |
111 | ||
112 | relative_path.push(backup_type); | |
113 | ||
114 | relative_path.push(backup_id); | |
115 | ||
116 | let dt = Utc.timestamp(backup_time, 0); | |
117 | let date_str = dt.format("%Y-%m-%dT%H:%M:%S").to_string(); | |
118 | ||
119 | println!("date: {}", date_str); | |
120 | ||
121 | relative_path.push(&date_str); | |
122 | ||
123 | ||
124 | let mut full_path = self.base_path(); | |
125 | full_path.push(&relative_path); | |
126 | ||
127 | std::fs::create_dir_all(&full_path)?; | |
128 | ||
129 | Ok(relative_path) | |
130 | } | |
131 | ||
3d5c11e5 | 132 | pub fn list_images(&self) -> Result<Vec<PathBuf>, Error> { |
ff3d3100 | 133 | let base = self.base_path(); |
3d5c11e5 DM |
134 | |
135 | let mut list = vec![]; | |
136 | ||
ff3d3100 | 137 | // fixme: walk into subdirs ... |
3d5c11e5 DM |
138 | for entry in std::fs::read_dir(base)? { |
139 | let entry = entry?; | |
140 | if entry.file_type()?.is_file() { | |
141 | let path = entry.path(); | |
142 | if let Some(ext) = path.extension() { | |
64e53b28 | 143 | if ext == "iidx" { |
3d5c11e5 | 144 | list.push(path); |
77703d95 DM |
145 | } else if ext == "aidx" { |
146 | list.push(path); | |
3d5c11e5 DM |
147 | } |
148 | } | |
149 | } | |
150 | } | |
151 | ||
152 | Ok(list) | |
153 | } | |
154 | ||
64e53b28 | 155 | fn mark_used_chunks(&self, status: &mut GarbageCollectionStatus) -> Result<(), Error> { |
3d5c11e5 DM |
156 | |
157 | let image_list = self.list_images()?; | |
158 | ||
159 | for path in image_list { | |
77703d95 DM |
160 | if let Some(ext) = path.extension() { |
161 | if ext == "iidx" { | |
bc616633 | 162 | let index = self.open_image_reader(&path)?; |
77703d95 DM |
163 | index.mark_used_chunks(status)?; |
164 | } else if ext == "aidx" { | |
bc616633 | 165 | let index = self.open_archive_reader(&path)?; |
77703d95 DM |
166 | index.mark_used_chunks(status)?; |
167 | } | |
168 | } | |
3d5c11e5 DM |
169 | } |
170 | ||
171 | Ok(()) | |
172 | } | |
173 | ||
03e4753d | 174 | pub fn garbage_collection(&self) -> Result<(), Error> { |
3d5c11e5 | 175 | |
a198d74f | 176 | if let Ok(ref mut _mutex) = self.gc_mutex.try_lock() { |
e95950e4 | 177 | |
64e53b28 DM |
178 | let mut gc_status = GarbageCollectionStatus::default(); |
179 | gc_status.used_bytes = 0; | |
6ea3a0b7 | 180 | |
64e53b28 DM |
181 | println!("Start GC phase1 (mark chunks)"); |
182 | ||
183 | self.mark_used_chunks(&mut gc_status)?; | |
184 | ||
185 | println!("Start GC phase2 (sweep unused chunks)"); | |
77703d95 | 186 | self.chunk_store.sweep_unused_chunks(&mut gc_status)?; |
64e53b28 DM |
187 | |
188 | println!("Used bytes: {}", gc_status.used_bytes); | |
189 | println!("Used chunks: {}", gc_status.used_chunks); | |
190 | println!("Disk bytes: {}", gc_status.disk_bytes); | |
191 | println!("Disk chunks: {}", gc_status.disk_chunks); | |
192 | ||
193 | } else { | |
194 | println!("Start GC failed - (already running/locked)"); | |
195 | } | |
3d5c11e5 DM |
196 | |
197 | Ok(()) | |
198 | } | |
529de6c7 | 199 | } |