Skip to content

Commit dbe96b1

Browse files
committed
feat: progress on sync
1 parent 27a8eb1 commit dbe96b1

3 files changed

Lines changed: 77 additions & 44 deletions

File tree

aw-sync/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ extern crate serde;
55
extern crate serde_json;
66

77
mod sync;
8-
pub use sync::sync_datastores;
8+
pub use sync::sync_run;

aw-sync/src/main.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,30 @@ extern crate chrono;
44
extern crate serde;
55
extern crate serde_json;
66

7+
use std::path::Path;
8+
9+
use aw_client_rust::AwClient;
10+
711
mod sync;
812

913
fn main() {
1014
// What needs to be done:
1115
// - [x] Setup local sync bucket
12-
// - Import local buckets and sync events from aw-server (either through API or through creating a read-only Datastore)
13-
// - Import buckets and sync events from remotes
16+
// - [x] Import local buckets and sync events from aw-server (either through API or through creating a read-only Datastore)
17+
// - [x] Import buckets and sync events from remotes
18+
// - [ ] Add CLI arguments
19+
// - [ ] For which local server to use
20+
// - [ ] For which sync dir to use
1421

1522
println!("Started aw-sync-rust...");
1623
aw_server::logging::setup_logger(true).expect("Failed to setup logging");
1724

18-
sync::sync_run();
25+
// TODO: Get path using dirs module
26+
let sync_directory = Path::new("sync-testing");
27+
28+
let client = AwClient::new("127.0.0.1", "5667", "aw-sync-rust");
29+
30+
sync::sync_run(sync_directory, client);
1931
info!("Finished successfully, exiting...");
2032

2133
// Needed to give the datastores some time to commit before program is shut down.

aw-sync/src/sync.rs

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub trait AccessMethod: std::fmt::Debug {
3434
) -> Result<Vec<Event>, String>;
3535
fn insert_events(&self, bucket_id: &str, events: Vec<Event>) -> Result<Vec<Event>, String>;
3636
fn get_event_count(&self, bucket_id: &str) -> Result<i64, String>;
37-
fn heartbeat(&self, bucket_id: &str, event: Event, duration: f64) -> Result<Event, String>;
37+
fn heartbeat(&self, bucket_id: &str, event: Event, duration: f64) -> Result<(), String>;
3838
}
3939

4040
impl AccessMethod for Datastore {
@@ -58,10 +58,10 @@ impl AccessMethod for Datastore {
5858
) -> Result<Vec<Event>, String> {
5959
Ok(self.get_events(bucket_id, start, end, limit).unwrap())
6060
}
61-
fn heartbeat(&self, bucket_id: &str, event: Event, duration: f64) -> Result<Event, String> {
62-
let res = self.heartbeat(bucket_id, event, duration).unwrap();
61+
fn heartbeat(&self, bucket_id: &str, event: Event, duration: f64) -> Result<(), String> {
62+
self.heartbeat(bucket_id, event, duration).unwrap();
6363
self.force_commit().unwrap();
64-
Ok(res)
64+
Ok(())
6565
}
6666
fn insert_events(&self, bucket_id: &str, events: Vec<Event>) -> Result<Vec<Event>, String> {
6767
let res = self.insert_events(bucket_id, &events[..]).unwrap();
@@ -114,39 +114,44 @@ impl AccessMethod for AwClient {
114114
Ok(())
115115
//Err(DatastoreError::InternalError("Not implemented".to_string()))
116116
}
117-
fn heartbeat(&self, _bucket_id: &str, _event: Event, _duration: f64) -> Result<Event, String> {
118-
Err("Not implemented".to_string())
117+
fn heartbeat(&self, bucket_id: &str, event: Event, duration: f64) -> Result<(), String> {
118+
self.heartbeat(bucket_id, &event, duration)
119+
.map_err(|e| format!("{:?}", e))
119120
}
120121
}
121122

122123
/// Performs a single sync pass
123-
pub fn sync_run() {
124-
// TODO: Get path using dirs module
125-
let sync_directory = Path::new("/tmp/aw-sync-rust/testing");
124+
pub fn sync_run(sync_directory: &Path, client: AwClient) {
126125
fs::create_dir_all(sync_directory).unwrap();
127126

128-
// TODO: Use the local datastore here, preferably passed from main
129-
let ds_local = AwClient::new("127.0.0.1", "5666", "aw-sync-rust");
130-
//let ds_local = Datastore::new(
131-
// sync_directory
132-
// .join("test-local.db")
133-
// .into_os_string()
134-
// .into_string()
135-
// .unwrap(),
136-
// false,
137-
//);
138-
info!("Set up local datastore");
139-
//log_buckets(&ds_local)?;
127+
// TODO: Get device id and use to name db file
128+
129+
let info = client.get_info().unwrap();
130+
let ds_localremote = Datastore::new(
131+
sync_directory
132+
.join(format!("test-{}.db", info.device_id))
133+
.into_os_string()
134+
.into_string()
135+
.unwrap(),
136+
false,
137+
);
138+
info!("Set up remote for local device");
140139

141140
let ds_remotes = setup_test(sync_directory).unwrap();
142-
info!("Set up remote datastores");
141+
info!("Set up remotes for testing");
143142

144-
// FIXME: These are not the datastores that should actually be synced, I'm just testing
143+
// Pull
144+
info!("Pulling...");
145145
for ds_from in &ds_remotes {
146-
sync_datastores(ds_from, &ds_local);
146+
sync_datastores(ds_from, &client, false);
147147
}
148148

149-
log_buckets(&ds_local);
149+
// Push local server buckets to sync folder
150+
info!("Pushing...");
151+
sync_datastores(&client, &ds_localremote, true);
152+
153+
log_buckets(&client);
154+
log_buckets(&ds_localremote);
150155
for ds_from in &ds_remotes {
151156
log_buckets(ds_from);
152157
}
@@ -166,14 +171,15 @@ fn setup_test(sync_directory: &Path) -> std::io::Result<Vec<Datastore>> {
166171
let ds = &ds_ as &dyn AccessMethod;
167172

168173
// Create a bucket
174+
// NOTE: Created with duplicate name to make sure it still works under such conditions
169175
let bucket_jsonstr = format!(
170176
r#"{{
171-
"id": "bucket-{}",
172-
"type": "test",
173-
"hostname": "device-{}",
174-
"client": "test"
175-
}}"#,
176-
n, n
177+
"id": "bucket",
178+
"type": "test",
179+
"hostname": "device-{}",
180+
"client": "test"
181+
}}"#,
182+
n
177183
);
178184
let bucket: Bucket = serde_json::from_str(&bucket_jsonstr)?;
179185
match ds.create_bucket(&bucket) {
@@ -212,9 +218,18 @@ fn setup_test(sync_directory: &Path) -> std::io::Result<Vec<Datastore>> {
212218
}
213219

214220
/// Returns the sync-destination bucket for a given bucket, creates it if it doesn't exist.
215-
fn get_or_create_sync_bucket(bucket_from: &Bucket, ds_to: &dyn AccessMethod) -> Bucket {
216-
// Ensure the bucket ID ends in "-synced"
217-
let new_id = format!("{}-synced", bucket_from.id.replace("-synced", ""));
221+
fn get_or_create_sync_bucket(
222+
bucket_from: &Bucket,
223+
ds_to: &dyn AccessMethod,
224+
is_push: bool,
225+
) -> Bucket {
226+
let new_id = if is_push {
227+
bucket_from.id.clone()
228+
} else {
229+
// Ensure the bucket ID ends in "-synced"
230+
let orig_bucketid = bucket_from.id.split("-synced-from-").next().unwrap();
231+
format!("{}-synced-from-{}", orig_bucketid, bucket_from.hostname)
232+
};
218233

219234
match ds_to.get_bucket(new_id.as_str()) {
220235
Ok(bucket) => bucket,
@@ -223,9 +238,10 @@ fn get_or_create_sync_bucket(bucket_from: &Bucket, ds_to: &dyn AccessMethod) ->
223238
bucket_new.id = new_id.clone();
224239
// TODO: Replace sync origin with hostname/GUID and discuss how we will treat the data
225240
// attributes for internal use.
226-
bucket_new
227-
.data
228-
.insert("$aw.sync.origin".to_string(), serde_json::json!("test"));
241+
bucket_new.data.insert(
242+
"$aw.sync.origin".to_string(),
243+
serde_json::json!(bucket_from.hostname),
244+
);
229245
ds_to.create_bucket(&bucket_new).unwrap();
230246
ds_to.get_bucket(new_id.as_str()).unwrap()
231247
}
@@ -234,16 +250,21 @@ fn get_or_create_sync_bucket(bucket_from: &Bucket, ds_to: &dyn AccessMethod) ->
234250
}
235251

236252
/// Syncs all buckets from `ds_from` to `ds_to` with `-synced` appended to the ID of the destination bucket.
237-
pub fn sync_datastores(ds_from: &dyn AccessMethod, ds_to: &dyn AccessMethod) {
253+
fn sync_datastores(ds_from: &dyn AccessMethod, ds_to: &dyn AccessMethod, is_push: bool) {
238254
// FIXME: "-synced" should only be appended when synced to the local database, not to the
239255
// staging area for local buckets.
240256
info!("Syncing {:?} to {:?}", ds_from, ds_to);
241257

242258
let buckets_from = ds_from.get_buckets().unwrap();
243259
for bucket_from in buckets_from.values() {
244-
let bucket_to = get_or_create_sync_bucket(bucket_from, ds_to);
260+
// TODO: Refuse to sync buckets without hostname/device ID set, or if set to 'unknown'
261+
if bucket_from.hostname == "unknown" {
262+
warn!("Bucket hostname/device ID was invalid, skipping");
263+
continue;
264+
}
265+
let bucket_to = get_or_create_sync_bucket(bucket_from, ds_to, is_push);
245266
let eventcount_to_old = ds_to.get_event_count(bucket_to.id.as_str()).unwrap();
246-
//info!("{:?}", bucket_to);
267+
info!("Bucket: {:?}", bucket_to.id);
247268

248269
// Sync events
249270
// FIXME: This should use bucket_to.metadata.end, but it doesn't because it doesn't work

0 commit comments

Comments
 (0)