@@ -26,7 +26,7 @@ use parking_lot::RwLock;
2626use redb:: { Database , ReadableTable , TableDefinition } ;
2727use serde:: { Deserialize , Serialize } ;
2828
29- use ledger_types:: { Hash , NamespaceId , ShardBlock , VaultEntry , VaultId , compute_tx_merkle_root} ;
29+ use ledger_types:: { Hash , NamespaceId , Operation , ShardBlock , ShardId , VaultEntry , VaultId , compute_tx_merkle_root} ;
3030
3131use crate :: types:: {
3232 BlockRetentionPolicy , LedgerNodeId , LedgerRequest , LedgerResponse , LedgerTypeConfig ,
@@ -35,6 +35,7 @@ use crate::types::{
3535
3636// Re-export storage types used in this module
3737use ledger_storage:: { BlockArchive , StateError , StateLayer } ;
38+ use ledger_storage:: system:: { NamespaceRegistry , NamespaceStatus , SystemKeys , SYSTEM_VAULT_ID } ;
3839
3940// ============================================================================
4041// Table Definitions
@@ -114,7 +115,7 @@ pub struct NamespaceMeta {
114115 /// Human-readable name.
115116 pub name : String ,
116117 /// Shard hosting this namespace (0 for default).
117- pub shard_id : u32 ,
118+ pub shard_id : ShardId ,
118119 /// Whether the namespace is deleted.
119120 pub deleted : bool ,
120121}
@@ -354,11 +355,11 @@ pub struct RaftLogStore {
354355 /// Applied state (state machine) - shared with accessor.
355356 applied_state : Arc < RwLock < AppliedState > > ,
356357 /// State layer for entity/relationship storage (shared with read service).
357- state_layer : Option < Arc < RwLock < StateLayer > > > ,
358+ state_layer : Option < Arc < StateLayer > > ,
358359 /// Block archive for permanent block storage.
359360 block_archive : Option < Arc < BlockArchive > > ,
360361 /// Shard ID for this Raft group.
361- shard_id : i32 ,
362+ shard_id : ShardId ,
362363 /// Node ID for block metadata.
363364 node_id : String ,
364365 /// Current shard height (for block creation).
@@ -411,7 +412,7 @@ impl RaftLogStore {
411412 }
412413
413414 /// Configure the state layer for transaction application.
414- pub fn with_state_layer ( mut self , state_layer : Arc < RwLock < StateLayer > > ) -> Self {
415+ pub fn with_state_layer ( mut self , state_layer : Arc < StateLayer > ) -> Self {
415416 self . state_layer = Some ( state_layer) ;
416417 self
417418 }
@@ -423,7 +424,7 @@ impl RaftLogStore {
423424 }
424425
425426 /// Configure shard metadata.
426- pub fn with_shard_config ( mut self , shard_id : i32 , node_id : String ) -> Self {
427+ pub fn with_shard_config ( mut self , shard_id : ShardId , node_id : String ) -> Self {
427428 self . shard_id = shard_id;
428429 self . node_id = node_id;
429430 self
@@ -435,7 +436,7 @@ impl RaftLogStore {
435436 }
436437
437438 /// Get a reference to the state layer (if configured).
438- pub fn state_layer ( & self ) -> Option < & Arc < RwLock < StateLayer > > > {
439+ pub fn state_layer ( & self ) -> Option < & Arc < StateLayer > > {
439440 self . state_layer . as_ref ( )
440441 }
441442
@@ -552,15 +553,14 @@ impl RaftLogStore {
552553 . unwrap_or ( ledger_types:: ZERO_HASH ) ;
553554
554555 // Apply transactions to state layer if configured
555- let state_root = if let Some ( state_layer_lock ) = & self . state_layer {
556+ let state_root = if let Some ( state_layer ) = & self . state_layer {
556557 // Collect all operations from all transactions
557558 let all_ops: Vec < _ > = transactions
558559 . iter ( )
559560 . flat_map ( |tx| tx. operations . clone ( ) )
560561 . collect ( ) ;
561562
562- // Acquire write lock and apply operations
563- let state_layer = state_layer_lock. write ( ) ;
563+ // Apply operations (StateLayer is internally thread-safe via redb MVCC)
564564 if let Err ( e) = state_layer. apply_operations ( * vault_id, & all_ops, new_height) {
565565 // Per DESIGN.md §6.1: On CAS failure, return current state for conflict resolution
566566 return match e {
@@ -655,18 +655,69 @@ impl RaftLogStore {
655655 )
656656 }
657657
658- LedgerRequest :: CreateNamespace { name } => {
658+ LedgerRequest :: CreateNamespace { name, shard_id } => {
659659 let namespace_id = state. sequences . next_namespace ( ) ;
660+ // Use provided shard_id or default to 0 (system shard)
661+ let assigned_shard = shard_id. unwrap_or ( 0 ) ;
660662 state. namespaces . insert (
661663 namespace_id,
662664 NamespaceMeta {
663665 namespace_id,
664666 name : name. clone ( ) ,
665- shard_id : 0 , // Default shard
667+ shard_id : assigned_shard ,
666668 deleted : false ,
667669 } ,
668670 ) ;
669- ( LedgerResponse :: NamespaceCreated { namespace_id } , None )
671+
672+ // Persist namespace to StateLayer for ShardRouter discovery.
673+ // This enables the ShardRouter to find the namespace->shard mapping.
674+ if let Some ( state_layer) = & self . state_layer {
675+ let registry = NamespaceRegistry {
676+ namespace_id,
677+ name : name. clone ( ) ,
678+ shard_id : assigned_shard,
679+ member_nodes : vec ! [ ] , // TODO: populate from cluster membership
680+ status : NamespaceStatus :: Active ,
681+ config_version : 1 ,
682+ created_at : chrono:: Utc :: now ( ) ,
683+ } ;
684+
685+ // Serialize and write to StateLayer
686+ if let Ok ( value) = postcard:: to_allocvec ( & registry) {
687+ let key = SystemKeys :: namespace_key ( namespace_id) ;
688+ let name_index_key = SystemKeys :: namespace_name_index_key ( name) ;
689+ let ops = vec ! [
690+ Operation :: SetEntity {
691+ key,
692+ value,
693+ condition: None ,
694+ expires_at: None ,
695+ } ,
696+ Operation :: SetEntity {
697+ key: name_index_key,
698+ value: namespace_id. to_string( ) . into_bytes( ) ,
699+ condition: None ,
700+ expires_at: None ,
701+ } ,
702+ ] ;
703+
704+ if let Err ( e) = state_layer. apply_operations ( SYSTEM_VAULT_ID , & ops, 0 ) {
705+ tracing:: error!(
706+ namespace_id,
707+ error = %e,
708+ "Failed to persist namespace to StateLayer"
709+ ) ;
710+ }
711+ }
712+ }
713+
714+ (
715+ LedgerResponse :: NamespaceCreated {
716+ namespace_id,
717+ shard_id : assigned_shard,
718+ } ,
719+ None ,
720+ )
670721 }
671722
672723 LedgerRequest :: CreateVault {
@@ -1196,7 +1247,6 @@ impl RaftStorage<LedgerTypeConfig> for RaftLogStore {
11961247
11971248 // Restore StateLayer entities if StateLayer is configured
11981249 if let Some ( state_layer) = & self . state_layer {
1199- let state = state_layer. write ( ) ;
12001250 for ( vault_id, entities) in & combined. vault_entities {
12011251 for entity in entities {
12021252 // Convert entity to SetEntity operation
@@ -1211,7 +1261,7 @@ impl RaftStorage<LedgerTypeConfig> for RaftLogStore {
12111261 } ,
12121262 } ] ;
12131263 // Apply at the entity's version height
1214- if let Err ( e) = state . apply_operations ( * vault_id, & ops, entity. version ) {
1264+ if let Err ( e) = state_layer . apply_operations ( * vault_id, & ops, entity. version ) {
12151265 tracing:: warn!(
12161266 vault_id,
12171267 key = %String :: from_utf8_lossy( & entity. key) ,
@@ -1240,14 +1290,14 @@ impl RaftStorage<LedgerTypeConfig> for RaftLogStore {
12401290 }
12411291
12421292 // Collect entities from StateLayer if configured
1293+ // StateLayer is internally thread-safe via redb's MVCC, so no lock needed
12431294 let vault_entities = if let Some ( state_layer) = & self . state_layer {
1244- let sl = state_layer. read ( ) ;
12451295 let mut entities_map = HashMap :: new ( ) ;
12461296
12471297 // Get entities for each known vault
12481298 for & ( namespace_id, vault_id) in state. vault_heights . keys ( ) {
12491299 // List all entities in this vault (up to 10000 per vault for snapshot)
1250- match sl . list_entities ( vault_id, None , None , 10000 ) {
1300+ match state_layer . list_entities ( vault_id, None , None , 10000 ) {
12511301 Ok ( entities) => {
12521302 if !entities. is_empty ( ) {
12531303 entities_map. insert ( vault_id, entities) ;
@@ -1387,12 +1437,13 @@ mod tests {
13871437
13881438 let request = LedgerRequest :: CreateNamespace {
13891439 name : "test-ns" . to_string ( ) ,
1440+ shard_id : None ,
13901441 } ;
13911442
13921443 let ( response, _vault_entry) = store. apply_request ( & request, & mut state) ;
13931444
13941445 match response {
1395- LedgerResponse :: NamespaceCreated { namespace_id } => {
1446+ LedgerResponse :: NamespaceCreated { namespace_id, .. } => {
13961447 assert_eq ! ( namespace_id, 1 ) ;
13971448 }
13981449 _ => panic ! ( "unexpected response" ) ,
@@ -1661,9 +1712,11 @@ mod tests {
16611712 let requests = vec ! [
16621713 LedgerRequest :: CreateNamespace {
16631714 name: "acme-corp" . to_string( ) ,
1715+ shard_id: None ,
16641716 } ,
16651717 LedgerRequest :: CreateNamespace {
16661718 name: "startup-inc" . to_string( ) ,
1719+ shard_id: None ,
16671720 } ,
16681721 LedgerRequest :: CreateVault {
16691722 namespace_id: 1 ,
@@ -1882,6 +1935,7 @@ mod tests {
18821935 let requests: Vec < LedgerRequest > = vec ! [
18831936 LedgerRequest :: CreateNamespace {
18841937 name: "ns1" . to_string( ) ,
1938+ shard_id: None ,
18851939 } ,
18861940 LedgerRequest :: CreateVault {
18871941 namespace_id: 1 ,
@@ -2025,6 +2079,7 @@ mod tests {
20252079 store. apply_request (
20262080 & LedgerRequest :: CreateNamespace {
20272081 name : "test" . to_string ( ) ,
2082+ shard_id : None ,
20282083 } ,
20292084 & mut state,
20302085 ) ;
0 commit comments