@@ -138,6 +138,14 @@ class IsolationLevel(IntEnum):
138138 SERIALIZABLE = 4
139139
140140
141+ class ObjStoreBackend (IntEnum ):
142+ """Object store backend types."""
143+
144+ BACKEND_FS = 0
145+ BACKEND_S3 = 1
146+ BACKEND_UNKNOWN = 99
147+
148+
141149class TidesDBError (Exception ):
142150 """Base exception for TidesDB errors."""
143151
@@ -227,6 +235,29 @@ class _CCommitOp(Structure):
227235COMMIT_HOOK_FUNC = CFUNCTYPE (c_int , POINTER (_CCommitOp ), c_int , c_uint64 , c_void_p )
228236
229237
238+ class _CObjStoreConfig (Structure ):
239+ """C structure for tidesdb_objstore_config_t."""
240+
241+ _fields_ = [
242+ ("local_cache_path" , c_char_p ),
243+ ("local_cache_max_bytes" , c_size_t ),
244+ ("cache_on_read" , c_int ),
245+ ("cache_on_write" , c_int ),
246+ ("max_concurrent_uploads" , c_int ),
247+ ("max_concurrent_downloads" , c_int ),
248+ ("multipart_threshold" , c_size_t ),
249+ ("multipart_part_size" , c_size_t ),
250+ ("sync_manifest_to_object" , c_int ),
251+ ("replicate_wal" , c_int ),
252+ ("wal_upload_sync" , c_int ),
253+ ("wal_sync_threshold_bytes" , c_size_t ),
254+ ("wal_sync_on_commit" , c_int ),
255+ ("replica_mode" , c_int ),
256+ ("replica_sync_interval_us" , c_uint64 ),
257+ ("replica_replay_wal" , c_int ),
258+ ]
259+
260+
230261class _CConfig (Structure ):
231262 """C structure for tidesdb_config_t."""
232263
@@ -526,6 +557,57 @@ class _CDbStats(Structure):
526557_lib .tidesdb_promote_to_primary .argtypes = [c_void_p ]
527558_lib .tidesdb_promote_to_primary .restype = c_int
528559
560+ _lib .tidesdb_objstore_default_config .argtypes = []
561+ _lib .tidesdb_objstore_default_config .restype = _CObjStoreConfig
562+
563+ _lib .tidesdb_objstore_fs_create .argtypes = [c_char_p ]
564+ _lib .tidesdb_objstore_fs_create .restype = c_void_p
565+
566+
567+ @dataclass
568+ class ObjStoreConfig :
569+ """Configuration for object store mode behavior."""
570+
571+ local_cache_path : str | None = None
572+ local_cache_max_bytes : int = 0
573+ cache_on_read : bool = True
574+ cache_on_write : bool = True
575+ max_concurrent_uploads : int = 4
576+ max_concurrent_downloads : int = 8
577+ multipart_threshold : int = 64 * 1024 * 1024
578+ multipart_part_size : int = 8 * 1024 * 1024
579+ sync_manifest_to_object : bool = True
580+ replicate_wal : bool = True
581+ wal_upload_sync : bool = False
582+ wal_sync_threshold_bytes : int = 1 * 1024 * 1024
583+ wal_sync_on_commit : bool = False
584+ replica_mode : bool = False
585+ replica_sync_interval_us : int = 5000000
586+ replica_replay_wal : bool = True
587+
588+ def _to_c_struct (self ) -> _CObjStoreConfig :
589+ """Convert to C structure."""
590+ c_cfg = _CObjStoreConfig ()
591+ c_cfg .local_cache_path = (
592+ self .local_cache_path .encode ("utf-8" ) if self .local_cache_path else None
593+ )
594+ c_cfg .local_cache_max_bytes = self .local_cache_max_bytes
595+ c_cfg .cache_on_read = 1 if self .cache_on_read else 0
596+ c_cfg .cache_on_write = 1 if self .cache_on_write else 0
597+ c_cfg .max_concurrent_uploads = self .max_concurrent_uploads
598+ c_cfg .max_concurrent_downloads = self .max_concurrent_downloads
599+ c_cfg .multipart_threshold = self .multipart_threshold
600+ c_cfg .multipart_part_size = self .multipart_part_size
601+ c_cfg .sync_manifest_to_object = 1 if self .sync_manifest_to_object else 0
602+ c_cfg .replicate_wal = 1 if self .replicate_wal else 0
603+ c_cfg .wal_upload_sync = 1 if self .wal_upload_sync else 0
604+ c_cfg .wal_sync_threshold_bytes = self .wal_sync_threshold_bytes
605+ c_cfg .wal_sync_on_commit = 1 if self .wal_sync_on_commit else 0
606+ c_cfg .replica_mode = 1 if self .replica_mode else 0
607+ c_cfg .replica_sync_interval_us = self .replica_sync_interval_us
608+ c_cfg .replica_replay_wal = 1 if self .replica_replay_wal else 0
609+ return c_cfg
610+
529611
530612@dataclass
531613class Config :
@@ -546,6 +628,8 @@ class Config:
546628 unified_memtable_skip_list_probability : float = 0.0
547629 unified_memtable_sync_mode : SyncMode = SyncMode .SYNC_NONE
548630 unified_memtable_sync_interval_us : int = 0
631+ object_store : c_void_p | None = None
632+ object_store_config : ObjStoreConfig | None = None
549633
550634
551635@dataclass
@@ -704,6 +788,57 @@ def default_config() -> Config:
704788 return Config (db_path = "" )
705789
706790
791+ def objstore_default_config () -> ObjStoreConfig :
792+ """Get default object store configuration from C library."""
793+ c_cfg = _lib .tidesdb_objstore_default_config ()
794+ path = None
795+ if c_cfg .local_cache_path :
796+ try :
797+ path = c_cfg .local_cache_path .decode ("utf-8" )
798+ except (UnicodeDecodeError , ValueError ):
799+ path = None
800+ return ObjStoreConfig (
801+ local_cache_path = path ,
802+ local_cache_max_bytes = c_cfg .local_cache_max_bytes ,
803+ cache_on_read = bool (c_cfg .cache_on_read ),
804+ cache_on_write = bool (c_cfg .cache_on_write ),
805+ max_concurrent_uploads = c_cfg .max_concurrent_uploads ,
806+ max_concurrent_downloads = c_cfg .max_concurrent_downloads ,
807+ multipart_threshold = c_cfg .multipart_threshold ,
808+ multipart_part_size = c_cfg .multipart_part_size ,
809+ sync_manifest_to_object = bool (c_cfg .sync_manifest_to_object ),
810+ replicate_wal = bool (c_cfg .replicate_wal ),
811+ wal_upload_sync = bool (c_cfg .wal_upload_sync ),
812+ wal_sync_threshold_bytes = c_cfg .wal_sync_threshold_bytes ,
813+ wal_sync_on_commit = bool (c_cfg .wal_sync_on_commit ),
814+ replica_mode = bool (c_cfg .replica_mode ),
815+ replica_sync_interval_us = c_cfg .replica_sync_interval_us ,
816+ replica_replay_wal = bool (c_cfg .replica_replay_wal ),
817+ )
818+
819+
820+ def objstore_fs_create (root_dir : str ) -> c_void_p :
821+ """
822+ Create a filesystem-backed object store connector.
823+
824+ Stores objects as files under root_dir mirroring the key path structure.
825+ Useful for testing and local replication.
826+
827+ Args:
828+ root_dir: Directory to store objects in
829+
830+ Returns:
831+ Opaque object store handle for use in Config.object_store
832+
833+ Raises:
834+ TidesDBError: If creation fails
835+ """
836+ handle = _lib .tidesdb_objstore_fs_create (root_dir .encode ("utf-8" ))
837+ if not handle :
838+ raise TidesDBError ("failed to create filesystem object store connector" , TDB_ERR_IO )
839+ return handle
840+
841+
707842def default_column_family_config () -> ColumnFamilyConfig :
708843 """Get default column family configuration from C library."""
709844 c_config = _lib .tidesdb_default_column_family_config ()
@@ -1424,12 +1559,25 @@ def __init__(self, config: Config) -> None:
14241559 """
14251560 self ._db : c_void_p | None = None
14261561 self ._closed = False
1562+ self ._objstore_config_ref : _CObjStoreConfig | None = None
14271563
14281564 os .makedirs (config .db_path , exist_ok = True )
14291565 abs_path = os .path .abspath (config .db_path )
14301566
14311567 self ._path_bytes = abs_path .encode ("utf-8" )
14321568
1569+ obj_store_ptr = None
1570+ obj_store_config_ptr = None
1571+
1572+ if config .object_store is not None :
1573+ obj_store_ptr = config .object_store
1574+
1575+ if config .object_store_config is not None :
1576+ self ._objstore_config_ref = config .object_store_config ._to_c_struct ()
1577+ obj_store_config_ptr = ctypes .cast (
1578+ ctypes .pointer (self ._objstore_config_ref ), c_void_p
1579+ )
1580+
14331581 c_config = _CConfig (
14341582 db_path = self ._path_bytes ,
14351583 num_flush_threads = config .num_flush_threads ,
@@ -1446,8 +1594,8 @@ def __init__(self, config: Config) -> None:
14461594 unified_memtable_skip_list_probability = config .unified_memtable_skip_list_probability ,
14471595 unified_memtable_sync_mode = int (config .unified_memtable_sync_mode ),
14481596 unified_memtable_sync_interval_us = config .unified_memtable_sync_interval_us ,
1449- object_store = None ,
1450- object_store_config = None ,
1597+ object_store = obj_store_ptr ,
1598+ object_store_config = obj_store_config_ptr ,
14511599 )
14521600
14531601 db_ptr = c_void_p ()
@@ -1476,6 +1624,8 @@ def open(
14761624 unified_memtable_skip_list_probability : float = 0.0 ,
14771625 unified_memtable_sync_mode : SyncMode = SyncMode .SYNC_NONE ,
14781626 unified_memtable_sync_interval_us : int = 0 ,
1627+ object_store : c_void_p | None = None ,
1628+ object_store_config : ObjStoreConfig | None = None ,
14791629 ) -> TidesDB :
14801630 """
14811631 Convenience method to open a database with individual parameters.
@@ -1496,6 +1646,8 @@ def open(
14961646 unified_memtable_skip_list_probability: Skip list probability for unified memtable
14971647 unified_memtable_sync_mode: Sync mode for unified memtable WAL
14981648 unified_memtable_sync_interval_us: Sync interval in microseconds for unified memtable
1649+ object_store: Object store connector handle (from objstore_fs_create())
1650+ object_store_config: Object store behavior configuration
14991651
15001652 Returns:
15011653 TidesDB instance
@@ -1516,6 +1668,8 @@ def open(
15161668 unified_memtable_skip_list_probability = unified_memtable_skip_list_probability ,
15171669 unified_memtable_sync_mode = unified_memtable_sync_mode ,
15181670 unified_memtable_sync_interval_us = unified_memtable_sync_interval_us ,
1671+ object_store = object_store ,
1672+ object_store_config = object_store_config ,
15191673 )
15201674 return cls (config )
15211675
0 commit comments