Skip to content

Commit b6b907c

Browse files
committed
Update dependencies and improve README examples for JsonMutexDB
1 parent c594899 commit b6b907c

File tree

5 files changed

+251
-165
lines changed

5 files changed

+251
-165
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ target/
2020

2121
/target
2222
test_db_perf.json
23+
test_db_async_coalesce.json
24+
test_db_sync_coalesce.json

Cargo.lock

Lines changed: 13 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "json-mutex-db"
3-
version = "0.0.1"
3+
version = "0.0.2"
44
edition = "2024"
55
authors = [ "Georgii Plotnikov<georgii@inferara.com>" ]
66
description = "Ridiculously simple, fast and thread safe JSON file database"
@@ -9,7 +9,8 @@ license = "Apache-2.0"
99
[dependencies]
1010
serde = { version = "1.0", features = ["derive"] }
1111
serde_json = "1.0"
12-
parking_lot = "0.12"
13-
crossbeam-channel = "0.5"
14-
simd-json = "0.15.0"
15-
tempfile = "3.19.1"
12+
parking_lot = "0.12.4"
13+
crossbeam-channel = "0.5.15"
14+
simd-json = "0.15.1"
15+
tempfile = "3.20.0"
16+

README.md

Lines changed: 63 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ cargo add json-mutex-db
3030
And get going in your code:
3131

3232
```rust
33-
use jsonmutexdb::{JsonMutexDB, DbError};
33+
use json_mutex_db::{JsonMutexDB, DbError};
3434
use serde::{Serialize, Deserialize};
3535
use serde_json::json;
3636

37-
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] // Needed for storage/retrieval
37+
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
3838
struct Config {
3939
api_key: Option<String>,
4040
retries: u32,
@@ -47,16 +47,16 @@ fn main() -> Result<(), DbError> {
4747

4848
// Get initial data (starts empty if file doesn't exist)
4949
let initial_val = db.get()?;
50-
println!("Initial value: {}", initial_val); // Outputs: {}
50+
println!("Initial value: {}", initial_val);
5151

5252
// Update the data - replace the whole value
5353
let initial_config = Config { api_key: None, retries: 3 };
54-
db.update(|data| {
54+
db.update(move |data| {
5555
*data = serde_json::to_value(&initial_config).unwrap();
5656
})?;
5757

5858
// Update part of the data (if it's an object)
59-
db.update(|data| {
59+
db.update(move |data| {
6060
if let Some(obj) = data.as_object_mut() {
6161
obj.insert("retries".to_string(), json!(5));
6262
obj.insert("new_feature_enabled".to_string(), json!(true));
@@ -66,38 +66,27 @@ fn main() -> Result<(), DbError> {
6666
// Get the current state
6767
let current_val = db.get()?;
6868
println!("Current value: {}", current_val);
69-
// Example Output: {"api_key":null,"new_feature_enabled":true,"retries":5}
7069

7170
// Try to deserialize it back
7271
let current_config: Config = serde_json::from_value(current_val.clone())
73-
.expect("Failed to deserialize");
74-
println!("Deserialized: {:?}", current_config);
72+
.expect("Failed to deserialize");
7573
assert_eq!(current_config.retries, 5);
7674

77-
7875
// Save it synchronously (atomic by default)
79-
println!("Saving data...");
8076
db.save_sync()?;
81-
println!("Data saved to {}", db_path);
8277

8378
// Cleanup the file for the example
8479
std::fs::remove_file(db_path).ok();
8580

8681
Ok(())
8782
}
88-
89-
// Remember to define your crate (replace with actual implementation)
90-
mod jsonmutexdb {
91-
// Paste the full JsonMutexDB implementation here...
92-
pub use crate::{JsonMutexDB, DbError}; // Assuming it's in src/lib.rs
93-
}
9483
```
9584

9685
## Configuration Options (new) ⚙️
9786

9887
When creating a JsonMutexDB, you have a few choices:
9988

100-
```rust
89+
```rust,ignore
10190
pub fn new(
10291
path: &str, // Path to the JSON file
10392
pretty: bool, // `true` for pretty-printed JSON, `false` for compact
@@ -115,89 +104,85 @@ pub fn new(
115104
### Async Updates
116105

117106
```rust
118-
use jsonmutexdb::JsonMutexDB;
107+
use json_mutex_db::{JsonMutexDB, DbError};
119108
use serde_json::json;
120-
use std::thread;
121109
use std::sync::Arc;
110+
use std::thread;
122111
use std::time::Duration;
123112

124-
# fn main() -> Result<(), Box<dyn std::error::Error>> { // Use Box<dyn Error> for example brevity
125-
let db_path = "async_example.json";
126-
// Enable async updates, use fast compact saving
127-
let db = Arc::new(JsonMutexDB::new(db_path, false, true, true)?);
128-
129-
let db_clone = Arc::clone(&db);
130-
thread::spawn(move || {
131-
println!("Background thread updating...");
132-
db_clone.update(|data| {
133-
let obj = data.as_object_mut().unwrap();
134-
obj.insert("worker_id".to_string(), json!(123));
135-
obj.insert("status".to_string(), json!("running"));
136-
}).expect("Failed to send update");
137-
println!("Background thread update sent.");
138-
});
139-
140-
// Give the background thread a moment to process
141-
thread::sleep(Duration::from_millis(50));
142-
143-
// Get the latest state (will block briefly to query background thread)
144-
let current_state = db.get()?;
145-
println!("State after async update: {}", current_state);
146-
assert_eq!(current_state["status"], "running");
147-
148-
db.save_sync()?; // Save the state fetched from background
149-
println!("Async state saved.");
150-
151-
// Required: Drop the Arc to signal background thread shutdown before cleanup
152-
drop(db);
153-
thread::sleep(Duration::from_millis(50)); // Allow time for shutdown/final save
154-
155-
std::fs::remove_file(db_path).ok();
156-
# Ok(())
157-
# }
158-
# mod jsonmutexdb { pub use crate::{JsonMutexDB, DbError}; } // Shim for example
159-
# use jsonmutexdb::{JsonMutexDB, DbError}; // Shim for example
113+
fn main() -> Result<(), DbError> {
114+
let db_path = "async_example.json";
115+
// Enable async updates, use fast compact saving
116+
let db = Arc::new(JsonMutexDB::new(db_path, false, true, true)?);
117+
118+
let db_clone = Arc::clone(&db);
119+
thread::spawn(move || {
120+
println!("Background thread updating...");
121+
db_clone.update(|data| {
122+
let obj = data.as_object_mut().unwrap();
123+
obj.insert("worker_id".to_string(), json!(123));
124+
obj.insert("status".to_string(), json!("running"));
125+
}).expect("Failed to send update");
126+
println!("Background thread update sent.");
127+
});
128+
129+
// Give the background thread a moment to process
130+
thread::sleep(Duration::from_millis(50));
131+
132+
// Get the latest state (will block briefly to query background thread)
133+
let current_state = db.get()?;
134+
println!("State after async update: {}", current_state);
135+
assert_eq!(current_state["status"], "running");
136+
137+
db.save_sync()?; // Save the state fetched from background
138+
println!("Async state saved.");
139+
140+
// Required: Drop the Arc to signal background thread shutdown before cleanup
141+
drop(db);
142+
thread::sleep(Duration::from_millis(50)); // Allow time for shutdown/final save
143+
144+
std::fs::remove_file(db_path).ok();
145+
146+
Ok(())
147+
}
160148
```
161149

162150
### Async Saving
163151

164152
```rust
165-
use jsonmutexdb::JsonMutexDB;
153+
use json_mutex_db::{JsonMutexDB, DbError};
166154
use serde_json::json;
167155
use std::thread;
168156
use std::time::Duration;
169157

170-
# fn main() -> Result<(), Box<dyn std::error::Error>> {
171-
let db_path = "async_save_example.json";
172-
// Sync updates, pretty printing
173-
let db = JsonMutexDB::new(db_path, true, false, false)?;
158+
fn main() -> Result<(), DbError> {
159+
let db_path = "async_save_example.json";
160+
// Sync updates, pretty printing
161+
let db = JsonMutexDB::new(db_path, true, false, false)?;
162+
163+
db.update(|d| *d = json!({"message": "Hello from async save!"}))?;
174164

175-
db.update(|d| *d = json!({"message": "Hello from async save!"}))?;
165+
println!("Triggering async save...");
166+
db.save_async()?; // Returns immediately
176167

177-
println!("Triggering async save...");
178-
db.save_async()?; // Returns immediately
168+
println!("Main thread doing other work...");
169+
thread::sleep(Duration::from_millis(100));
179170

180-
println!("Main thread doing other work...");
181-
// In a real app, the main thread continues here.
182-
// We sleep just to allow the save to likely complete for the example.
183-
thread::sleep(Duration::from_millis(100));
171+
println!("Checking file...");
172+
let content = std::fs::read_to_string(db_path)?;
173+
println!("File content:\n{}", content);
174+
assert!(content.contains(" \"message\":")); // Check for pretty printing
184175

185-
println!("Checking file...");
186-
let content = std::fs::read_to_string(db_path)?;
187-
println!("File content:\n{}", content);
188-
assert!(content.contains(" \"message\":")); // Check for pretty printing
176+
std::fs::remove_file(db_path).ok();
189177

190-
std::fs::remove_file(db_path).ok();
191-
# Ok(())
192-
# }
193-
# mod jsonmutexdb { pub use crate::{JsonMutexDB, DbError}; } // Shim for example
194-
# use jsonmutexdb::{JsonMutexDB, DbError}; // Shim for example
178+
Ok(())
179+
}
195180
```
196181

197182
## Performance Notes ⚡️
198183

199184
* Atomic Sync Saves (`save_sync`): No longer deep-clones the JSON data to avoid extra allocations and copying. Instead, holds a read lock during serialization into a thread-local buffer, reducing memory operations at the cost of blocking concurrent updates during the save.
200-
* Asynchronous Saves (`save_async`): Clones the entire JSON data for background saving to avoid blocking the main thread during the save operation.
185+
* Asynchronous Saves (`save_async`): No longer deep-clones the JSON data; holds a read lock during serialization into a thread-local buffer in the background thread, reducing memory operations at the cost of blocking concurrent updates until the save completes.
201186
* Serialization Buffer: The thread-local buffer is pre-allocated based on initial file size to minimize reallocations.
202187
* I/O: Saves serialize into a thread-local in-memory buffer and issue a single `write_all` + `flush`, drastically reducing the number of write syscalls. Atomic saves still involve writing to a temporary file and renaming.
203188
* Async Updates: Updates are non-blocking and queued to a background thread. Multiple rapid updates are coalesced into a single disk write, reducing redundant I/O.

0 commit comments

Comments
 (0)