11mod storage;
22mod network;
3+ mod tokenizer;
34
45use actix_web:: { web, App , HttpServer } ;
56use once_cell:: sync:: OnceCell ;
67use reqwest;
78use serde_json:: Value ;
89use std:: collections:: HashMap ;
910use std:: env;
10- use std:: fs:: OpenOptions ;
1111use std:: process;
1212use std:: sync:: Mutex ;
1313
1414use 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.
2120static 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]
4361async 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