Skip to content

Commit 0388477

Browse files
author
test
committed
Initial
1 parent fb915a5 commit 0388477

8 files changed

Lines changed: 344 additions & 189 deletions

File tree

.idea/DynaRust.iml

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

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ serde_json = "1.0.140"
1212
serde = { version = "1.0.219", features = ["derive"] }
1313
base64 = "0.21"
1414
once_cell = "1.21.3"
15+
regex = "1.11.1"
1516

src/main.rs

Lines changed: 79 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,62 @@
11
mod storage;
22
mod network;
3+
mod tokenizer;
34

45
use actix_web::{web, App, HttpServer};
56
use once_cell::sync::OnceCell;
67
use reqwest;
78
use serde_json::Value;
89
use std::collections::HashMap;
910
use std::env;
10-
use std::fs::OpenOptions;
1111
use std::process;
1212
use std::sync::Mutex;
1313

1414
use storage::engine::{
15-
AppState, ClusterData, delete_value, get_value, get_store, join_cluster,
16-
put_value,
15+
AppState, ClusterData, delete_value, get_value, get_table_store, join_cluster, put_value,
1716
};
18-
use storage::persistance::{cold_save, load_db};
17+
use storage::persistance::{cold_save, load_all_tables};
1918

20-
// Declare APP_STATE globally so that it’s available throughout the module.
19+
/// Declare APP_STATE globally so that it’s available throughout the module.
2120
static APP_STATE: OnceCell<web::Data<AppState>> = OnceCell::new();
2221

23-
/// Merge the remote state into the local state.
24-
/// For each key from the remote state:
22+
/// Merge the remote state for a given table into the local state.
23+
/// For each key (i.e. partition) from the remote state:
2524
/// - If the key already exists, update its attributes.
2625
/// - Otherwise, insert the new key with its attributes.
27-
fn merge_state(
26+
fn merge_table_state(
2827
local: &mut HashMap<String, HashMap<String, Value>>,
2928
remote: HashMap<String, HashMap<String, Value>>,
3029
) {
3130
for (key, remote_partition) in remote {
32-
local.entry(key)
31+
local
32+
.entry(key)
3333
.and_modify(|local_partition| {
34-
for (attr, value) in remote_partition.clone() {
35-
local_partition.insert(attr, value);
34+
for (attr, value) in remote_partition.iter() {
35+
local_partition.insert(attr.clone(), value.clone());
3636
}
3737
})
3838
.or_insert(remote_partition);
3939
}
4040
}
4141

42+
/// Merge the entire global remote store into the local store.
43+
/// Both stores have the type:
44+
/// HashMap<table_name, HashMap<partition_key, HashMap<attribute, Value>>>
45+
fn merge_global_store(
46+
local: &mut HashMap<String, HashMap<String, HashMap<String, Value>>>,
47+
remote: HashMap<String, HashMap<String, HashMap<String, Value>>>,
48+
) {
49+
for (table_name, remote_table) in remote {
50+
local
51+
.entry(table_name.clone())
52+
.and_modify(|local_table| {
53+
// Clone remote_table so we don't move it
54+
merge_table_state(local_table, remote_table.clone());
55+
})
56+
.or_insert(remote_table);
57+
}
58+
}
59+
4260
#[actix_web::main]
4361
async fn main() -> std::io::Result<()> {
4462
// Usage: <program> <current_node_address> [join_node_address]
@@ -51,33 +69,37 @@ async fn main() -> std::io::Result<()> {
5169
process::exit(1);
5270
}
5371
let current_node = args[1].clone();
54-
// Create a separate clone for the bind operation.
72+
// Use the current node address for binding.
5573
let bind_addr = current_node.clone();
5674

57-
// Initialize the local in-memory key–value store.
75+
// Initialize the local in‑memory multi‑table key–value store.
76+
// Each table name maps to a key–value store:
77+
// table_name -> (key -> attributes)
5878
let state = web::Data::new(AppState {
79+
// Specify a base directory for table folders.
80+
base_dir: "./data",
5981
store: Mutex::new(HashMap::new()),
6082
});
6183
// Set the global APP_STATE pointer.
62-
match APP_STATE.set(state.clone()) {
63-
Ok(x) => x,
64-
Err(_) => panic!("Failed to set APP_STATE"),
65-
};
66-
67-
// Open and load local cold storage from disk.
68-
let mut file = OpenOptions::new()
69-
.read(true)
70-
.write(true)
71-
.create(true)
72-
.open("storage.db")?;
73-
match load_db(&mut file, &state) {
84+
let _ = APP_STATE.set(state.clone());
85+
86+
// Load local cold storage from disk by enumerating each table folder.
87+
match load_all_tables(&state) {
7488
Ok(_) => println!("Cold storage loaded"),
75-
Err(e) => eprintln!("Error loading DB: {}", e),
89+
Err(e) => eprintln!("Error loading cold storage: {}", e),
7690
}
7791

78-
// If a join node is provided, join its cluster and pull its state.
92+
// (Optionally) Ensure the default table exists in memory.
93+
{
94+
let default_table = "default".to_string();
95+
let mut store = state.store.lock().unwrap();
96+
store.entry(default_table).or_insert(HashMap::new());
97+
}
98+
99+
// If a join node is provided, join its cluster and pull its global state.
79100
if args.len() >= 3 {
80101
let join_node = args[2].clone();
102+
81103
// Send join request.
82104
let client = reqwest::Client::new();
83105
let join_url = format!("http://{}/join", join_node);
@@ -102,42 +124,39 @@ async fn main() -> std::io::Result<()> {
102124
Err(e) => println!("Error joining cluster: {}", e),
103125
}
104126

105-
// Pull the remote state through the /store endpoint.
106-
let client = reqwest::Client::new();
127+
// Pull the remote state for all tables.
128+
// This requires that the remote node exposes a global state endpoint at GET /store.
107129
let store_url = format!("http://{}/store", join_node);
130+
let client = reqwest::Client::new();
108131
match client.get(&store_url).send().await {
109132
Ok(resp) => {
110133
if resp.status().is_success() {
111-
// The remote data is a mapping from partition keys
112-
// to JSON maps.
113-
let remote_store: HashMap<String, HashMap<String, Value>> =
114-
match resp.json().await {
115-
Ok(store) => store,
116-
Err(e) => {
117-
eprintln!("Failed to parse cold storage: {}", e);
118-
HashMap::new()
119-
}
120-
};
121-
// Merge the remote store into the current local store.
134+
let remote_store: HashMap<
135+
String,
136+
HashMap<String, HashMap<String, Value>>,
137+
> = resp.json().await.unwrap_or_else(|e| {
138+
eprintln!("Failed to parse global cold storage: {}", e);
139+
HashMap::new()
140+
});
122141
{
123-
let mut local_store = state.store.lock().unwrap();
124-
merge_state(&mut local_store, remote_store);
142+
let mut store = state.store.lock().unwrap();
143+
merge_global_store(&mut store, remote_store);
125144
println!(
126-
"Merged cold storage from {}: {:?}",
127-
join_node, *local_store
145+
"Merged global cold storage from {}: {:?}",
146+
join_node, *store
128147
);
129148
}
130149
} else {
131150
eprintln!(
132-
"Failed to get cold storage from {}: {:?}",
151+
"Failed to get global cold storage from {}: {:?}",
133152
join_node,
134153
resp.status()
135154
);
136155
}
137156
}
138157
Err(e) => {
139158
eprintln!(
140-
"Error fetching cold storage from {}: {}",
159+
"Error fetching global cold storage from {}: {}",
141160
join_node, e,
142161
);
143162
}
@@ -148,7 +167,7 @@ async fn main() -> std::io::Result<()> {
148167
tokio::spawn({
149168
let state = state.clone();
150169
async move {
151-
cold_save(state, 60).await;
170+
cold_save(state, 30).await;
152171
}
153172
});
154173

@@ -168,13 +187,15 @@ async fn main() -> std::io::Result<()> {
168187

169188
println!("Starting distributed DB engine at http://{}", current_node);
170189

171-
// Build and run the HTTP server with the /store endpoint.
190+
// Build and run the HTTP server.
172191
HttpServer::new(move || {
173192
App::new()
174193
.app_data(state.clone())
175194
.app_data(cluster_data.clone())
176195
.app_data(web::Data::new(current_node.clone()))
196+
// Endpoint to join the cluster.
177197
.route("/join", web::post().to(join_cluster))
198+
// Endpoints for updating/fetching membership.
178199
.route(
179200
"/membership",
180201
web::get().to(network::broadcaster::get_membership),
@@ -183,10 +204,16 @@ async fn main() -> std::io::Result<()> {
183204
"/update_membership",
184205
web::post().to(network::broadcaster::update_membership),
185206
)
186-
.route("/key/{key}", web::get().to(get_value))
187-
.route("/key/{key}", web::put().to(put_value))
188-
.route("/key/{key}", web::delete().to(delete_value))
189-
.route("/store", web::get().to(get_store))
207+
// Key endpoints with multi‑table support: the table name is part of the URL.
208+
.route("/{table}/key/{key}", web::get().to(get_value))
209+
.route("/{table}/key/{key}", web::put().to(put_value))
210+
.route("/{table}/key/{key}", web::delete().to(delete_value))
211+
// Endpoint to fetch a table’s entire in‑memory store.
212+
.route("/{table}/store", web::get().to(get_table_store))
213+
// Global endpoint returning the entire in‑memory store.
214+
.route("/store", web::get().to(|state: web::Data<AppState>| async move {
215+
web::Json(state.store.lock().unwrap().clone())
216+
}))
190217
})
191218
.bind(bind_addr.as_str())?
192219
.run()

0 commit comments

Comments
 (0)