rocksolid 2.5.4

An ergonomic, high-level RocksDB wrapper for Rust. Features CF-aware optimistic & pessimistic transactions, advanced routing for merge operators and compaction filters, performance tuning profiles, batching, TTL values, and DAO macros.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
# RockSolid Store - Usage Guide & API Overview

This guide provides detailed examples and a comprehensive overview of how to use the `rocksolid` library for common and advanced database tasks. For an exhaustive API list of all public types, traits, and functions, please refer to the [official rustdoc documentation](https://docs.rs/rocksolid/).

## Table of Contents

1.  [Core Concepts Recap]#core-concepts-recap
2.  [Quick Start: Default Column Family (Non-Transactional)]#quick-start-default-column-family-non-transactional
3.  [Quick Start: Column Families (Non-Transactional)]#quick-start-column-families-non-transactional
4.  [Quick Start: Transactional Usage (Default CF)]#quick-start-transactional-usage-default-cf
5.  [Quick Start: Transactional Usage (CF-Aware)]#quick-start-transactional-usage-cf-aware
6.  [Detailed Configuration]#detailed-configuration
    *   [Non-Transactional CF-Aware (`RocksDbCFStoreConfig`)]#non-transactional-cf-aware-rocksdbcfstoreconfig
    *   [Non-Transactional Default-CF (`RocksDbStoreConfig`)]#non-transactional-default-cf-rocksdbsstoreconfig
    *   [Transactional CF-Aware (`RocksDbCFTxnStoreConfig`)]#transactional-cf-aware-rocksdbtcftxnstoreconfig
    *   [Transactional Default-CF (`RocksDbTxnStoreConfig`)]#transactional-default-cf-rocksdbtxnstoreconfig
    *   [Comparators (`RockSolidComparatorOpt`)]#comparators-rocksolidcomparatoropt
7.  [Opening & Managing Stores]#opening--managing-stores
8.  [CF-Aware Operations (`CFOperations` Trait)]#cf-aware-operations-cfoperations-trait
    *   [Basic CRUD & Read Operations]#basic-crud--read-operations
    *   [Raw Byte Operations]#raw-byte-operations
    *   [Operations with Expiry]#operations-with-expiry
    *   [Multi-Get Operations]#multi-get-operations
    *   [Merge Operations (Method Call)]#merge-operations-method-call
    *   [Range Deletion]#range-deletion
    *   [Unified Iteration API]#unified-iteration-api
    *   [Find Operations]#find-operations
9.  [Default Column Family Operations (`DefaultCFOperations` Trait)]#default-column-family-operations-defaultcfoperations-trait
10. [Batch Operations (`BatchWriter`)]#batch-operations-batchwriter
11. [Transactional Operations]#transactional-operations
    *   [`RocksDbCFTxnStore` (CF-Aware Transactions)]#rocksdbtcftxnstore-cf-aware-transactions
    *   [`RocksDbTxnStore` (Default-CF Focused Transactions)]#rocksdbtxnstore-default-cf-focused-transactions
12. [Merge Operator Configuration & Usage]#merge-operator-configuration--usage
13. [Compaction Filter Configuration & Usage]#compaction-filter-configuration--usage
14. [Helper Macros (`macros.rs`)]#helper-macros-macrosrs
15. [Utilities (`utils.rs`)]#utilities-utilsrs
16. [Error Handling (`StoreError`, `StoreResult`)]#error-handling-storeerror-storeresult
17. [Contributing Focus Areas]#contributing-focus-areas

---

## 1. Core Concepts Recap

*   **`RocksDbCFStore`**: Primary non-transactional CF-aware store.
*   **`RocksDbStore`**: Wrapper for non-transactional default CF operations.
*   **`RocksDbCFTxnStore`**: Primary **pessimistic** transactional CF-aware store.
*   **`RocksDbTxnStore`**: Wrapper for **pessimistic** transactional default CF operations.
*   **`RocksDbCFOptimisticTxnStore`**: Primary **optimistic** transactional CF-aware store.
*   **`RocksDbOptimisticTxnStore`**: Wrapper for **optimistic** transactional default CF operations.
*   **Column Families (CFs)**: Independent keyspaces within a single RocksDB database. Operations explicitly specify a CF name (e.g., `"my_cf"`, or `rocksdb::DEFAULT_COLUMN_FAMILY_NAME` for the default CF).
*   **Keys (`SerKey`, `K` types in method signatures)**:
    *   User-provided keys must implement `rocksolid::bytes::AsBytes` (e.g., `String`, `&str`, `Vec<u8>`).
    *   These are serialized to raw byte slices (`&[u8]`) for RocksDB.
*   **Values (`V`, `OutV` types)**:
    *   User-provided values must implement `serde::Serialize`.
    *   Retrieved values must implement `serde::de::DeserializeOwned`.
    *   Values are internally serialized/deserialized using `rmp-serde` (MessagePack) by default.
*   **Output Keys from Iteration (`OutK`)**:
    *   If deserializing keys from raw bytes (e.g., in `find_by_prefix`), `OutK` must implement `bytevec::ByteDecodable` and `serde::de::DeserializeOwned`.
*   **`StoreResult<T>`**: Standard `Result<T, StoreError>` for all fallible operations.
*   **`TuningProfile`**: Pre-defined RocksDB option sets (e.g., `LatestValue`, `MemorySaver`, `TimeSeries`) for common workloads.
*   **`RockSolidComparatorOpt`**: For configuring custom key sorting logic per CF (e.g., natural sort).
*   **`TransactionContext<'store>`**: A helper for managing operations within a single **pessimistic** transaction on the default CF when using `RocksDbTxnStore`. Provides RAII for rollback.
*   **`OptimisticTransactionContext<'store>`**: A helper for managing operations within a single **optimistic** transaction. Does **not** provide automatic retries; application must handle commit conflicts.
*   **`optimistic_transaction()` Builder**: A fluent API on `RocksDbCFOptimisticTxnStore` for building and executing optimistic transactions with **automatic conflict detection and retries**. This is the recommended approach for optimistic transactions.
*   **`Tx<'a>`**: An alias for `rocksdb::Transaction<'a, rocksdb::TransactionDB>`, representing a raw pessimistic transaction object.
*   **`ValueWithExpiry<Val>`**: Struct to store a value along with its Unix timestamp for expiration. Actual data removal requires a compaction filter.
*   **`MergeValue<PatchVal>`**: Represents a merge operation, combining an operator (e.g., Add, Union) and a patch value.
*   **Merge & Compaction Routers**: Builders (`MergeRouterBuilder`, `CompactionFilterRouterBuilder`) allow defining key-pattern based routing to custom logic for merge operations or compaction filters.
    *   ⚠️ **Router Warning**: Router functions (for merge and compaction filters) use globally shared static state. Ensure unique key patterns or prefixes if using the same routed operator name across different CFs or database instances to avoid unintended behavior.

---

## 2. Quick Start: Default Column Family (Non-Transactional)

For simple use cases focusing on the default Column Family:
```rust
use rocksolid::{RocksDbStore, config::RocksDbStoreConfig, store::DefaultCFOperations, StoreResult};
use serde::{Serialize, Deserialize};
use tempfile::tempdir; // For example path

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct User { id: u32, name: String }

fn main() -> StoreResult<()> {
    let temp_dir = tempdir().expect("Failed to create temp dir");
    let db_path = temp_dir.path().join("quickstart_default_db");

    // 1. Configure the store
    let config = RocksDbStoreConfig {
        path: db_path.to_str().unwrap().to_string(),
        create_if_missing: true,
        ..Default::default()
    };

    // 2. Open the store
    let store = RocksDbStore::open(config)?;

    // 3. Basic Operations (implicitly on default CF)
    let user = User { id: 1, name: "Alice".to_string() };
    let user_key = format!("user:{}", user.id);

    store.put(&user_key, &user)?;
    let retrieved_user: Option<User> = store.get(&user_key)?;
    assert_eq!(retrieved_user.as_ref(), Some(&user));
    println!("Retrieved User: {:?}", retrieved_user);

    Ok(())
}
```

---

## 3. Quick Start: Column Families (Non-Transactional)

For applications requiring multiple Column Families:
```rust
use rocksolid::cf_store::{RocksDbCFStore, CFOperations};
use rocksolid::config::{RocksDbCFStoreConfig, BaseCfConfig};
use rocksolid::StoreResult;
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use tempfile::tempdir;

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct Product { sku: String, price: f64 }

const PRODUCTS_CF: &str = "products_cf";

fn main() -> StoreResult<()> {
    let temp_dir = tempdir().expect("Failed to create temp dir");
    let db_path = temp_dir.path().join("quickstart_cf_db");

    // 1. Configure the CF-aware store
    let mut cf_configs = HashMap::new();
    cf_configs.insert(PRODUCTS_CF.to_string(), BaseCfConfig::default());
    // Always good practice to explicitly configure the default CF if it's to be opened.
    cf_configs.insert(rocksdb::DEFAULT_COLUMN_FAMILY_NAME.to_string(), BaseCfConfig::default());

    let config = RocksDbCFStoreConfig {
        path: db_path.to_str().unwrap().to_string(),
        create_if_missing: true,
        column_families_to_open: vec![
            rocksdb::DEFAULT_COLUMN_FAMILY_NAME.to_string(),
            PRODUCTS_CF.to_string(),
        ],
        column_family_configs: cf_configs,
        ..Default::default()
    };

    // 2. Open the CF-aware store
    let store = RocksDbCFStore::open(config)?;

    // 3. Operations on specific CFs
    let laptop = Product { sku: "LP100".to_string(), price: 1200.00 };
    store.put(PRODUCTS_CF, &laptop.sku, &laptop)?;

    let retrieved_product: Option<Product> = store.get(PRODUCTS_CF, "LP100")?;
    assert_eq!(retrieved_product.as_ref(), Some(&laptop));
    println!("Product: {:?}", retrieved_product);

    Ok(())
}
```
---

## 4. Quick Start: Transactional Usage (Default CF)
```rust
use rocksolid::{RocksDbTxnStore, tx::tx_store::RocksDbTxnStoreConfig, StoreResult, StoreError};
use rocksolid::store::DefaultCFOperations; // For store.put, store.get on committed state
use serde::{Serialize, Deserialize};
use tempfile::tempdir;

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct Account { id: String, balance: i64 }

// Operations within a transaction use TransactionContext
fn transfer_funds(store: &RocksDbTxnStore, from_acc_key: &str, to_acc_key: &str, amount: i64) -> StoreResult<()> {
    let mut ctx = store.transaction_context(); // Operates on default CF

    let mut from_account: Account = ctx.get(from_acc_key)?
        .ok_or_else(|| StoreError::Other(format!("Account not found: {}", from_acc_key)))?;
    let mut to_account: Account = ctx.get(to_acc_key)?
        .ok_or_else(|| StoreError::Other(format!("Account not found: {}", to_acc_key)))?;

    if from_account.balance < amount {
        ctx.rollback()?; // Explicitly rollback if condition fails
        return Err(StoreError::Other("Insufficient funds".into()));
    }
    from_account.balance -= amount;
    to_account.balance += amount;

    ctx.set(from_acc_key, &from_account)?;
    ctx.set(to_acc_key, &to_account)?;
    ctx.commit()?; // Commit all staged operations
    Ok(())
}

fn main() -> StoreResult<()> {
    let temp_dir = tempdir().expect("Failed to create temp dir");
    let db_path = temp_dir.path().join("txn_default_cf_db");

    let config = RocksDbTxnStoreConfig {
        path: db_path.to_str().unwrap().to_string(),
        create_if_missing: true,
        ..Default::default()
    };

    let txn_store = RocksDbTxnStore::open(config)?;

    // Initial setup (auto-committed)
    txn_store.put("acc:A", &Account { id: "A".into(), balance: 100 })?;
    txn_store.put("acc:B", &Account { id: "B".into(), balance: 50 })?;

    match transfer_funds(&txn_store, "acc:A", "acc:B", 20) {
        Ok(_) => println!("Transfer successful!"),
        Err(e) => eprintln!("Transfer failed: {}", e),
    }

    // Verify committed state
    let acc_a_final: Option<Account> = txn_store.get("acc:A")?;
    println!("Final balance for acc:A: {:?}", acc_a_final);
    assert_eq!(acc_a_final.unwrap().balance, 80);

    Ok(())
}
```

---

## 5. Quick Start: Transactional Usage (CF-Aware)

There are two modes for transactions: Pessimistic (locking) and Optimistic (detect-and-retry).

### Pessimistic (CF-Aware)

Ideal for high-contention workloads where waiting for a lock is better than repeatedly retrying.

```rust
use rocksolid::tx::{RocksDbCFTxnStore, cf_tx_store::{RocksDbTransactionalStoreConfig, CFTxConfig}};
use rocksolid::config::BaseCfConfig;
use rocksolid::StoreResult;
use rocksolid::CFOperations;
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use tempfile::tempdir;

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct Order { id: String, status: String }

const ORDERS_CF: &str = "orders_cf";

// Operations within a transaction use the provided &Tx object
fn process_order(store: &RocksDbCFTxnStore, order_id: &str, new_status: &str) -> StoreResult<()> {
    store.execute_transaction(None, |txn| {
        let mut order: Order = store.get_in_txn(txn, ORDERS_CF, order_id)?
            .ok_or_else(|| rocksolid::StoreError::Other("Order not found".into()))?;
        order.status = new_status.to_string();
        store.put_in_txn_cf(txn, ORDERS_CF, &order.id, &order)?;
        Ok(()) // Return Ok from the closure to commit
    })
}

fn main() -> StoreResult<()> {
    let temp_dir = tempdir().expect("Failed to create temp dir");
    let db_path = temp_dir.path().join("pessimistic_cf_db");

    let mut cf_configs = HashMap::new();
    cf_configs.insert(ORDERS_CF.to_string(), CFTxConfig { base_config: BaseCfConfig::default() });

    // Use the unified transactional config
    let config = RocksDbTransactionalStoreConfig {
        path: db_path.to_str().unwrap().to_string(),
        create_if_missing: true,
        column_families_to_open: vec![ORDERS_CF.to_string()],
        column_family_configs: cf_configs,
        // Specify the Pessimistic engine
        engine: rocksolid::tx::cf_tx_store::TransactionalEngine::Pessimistic(Default::default()),
        ..Default::default()
    };
    let cf_txn_store = RocksDbCFTxnStore::open(config)?;

    cf_txn_store.put(ORDERS_CF, "order123", &Order {id: "order123".into(), status: "Pending".into()})?;
    process_order(&cf_txn_store, "order123", "Shipped")?;
    println!("Pessimistic CF transaction processed.");

    Ok(())
}
```

### Optimistic (CF-Aware)

Ideal for low-contention workloads where transactions rarely conflict. Uses a detect-and-retry pattern.

```rust
use rocksolid::{
  tx::{
    cf_optimistic_tx_store::{RocksDbCFOptimisticTxnStore, RocksDbCFOptimisticTxnStoreConfig},
    policies::FixedRetry,
  },
  CFOperations, StoreError, StoreResult,
  serialization::{deserialize_value, serialize_value},
};
use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use tempfile::tempdir;

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct Account { balance: i64, version: u64 }

const ACCOUNTS_CF: &str = "accounts";

fn transfer(store: &RocksDbCFOptimisticTxnStore, from: &str, to: &str, amount: i64) -> StoreResult<()> {
    store.optimistic_transaction()
        .with_retry_policy(FixedRetry { max_attempts: 5, backoff: std::time::Duration::from_millis(10) })
        .execute_with_snapshot(|txn| {
            let cf_handle = store.get_cf_handle(ACCOUNTS_CF)?;
            
            let from_bytes = txn.get_pinned_cf(&cf_handle, from)?.ok_or_else(|| StoreError::Other("from account not found".into()))?;
            let mut from_acct: Account = deserialize_value(&from_bytes)?;
            
            let to_bytes = txn.get_pinned_cf(&cf_handle, to)?.ok_or_else(|| StoreError::Other("to account not found".into()))?;
            let mut to_acct: Account = deserialize_value(&to_bytes)?;

            if from_acct.balance < amount {
                return Err(StoreError::Other("Insufficient funds".into()));
            }
            from_acct.balance -= amount;
            from_acct.version += 1;
            to_acct.balance += amount;
            to_acct.version += 1;

            txn.put_cf(&cf_handle, from, serialize_value(&from_acct)?)?;
            txn.put_cf(&cf_handle, to, serialize_value(&to_acct)?)?;
            Ok(())
        })
}

fn main() -> StoreResult<()> {
    let temp_dir = tempdir().expect("Failed to create temp dir");
    let db_path = temp_dir.path().join("optimistic_cf_db");

    let mut cf_configs = HashMap::new();
    cf_configs.insert(ACCOUNTS_CF.to_string(), Default::default());

    let config = RocksDbCFOptimisticTxnStoreConfig {
        path: db_path.to_str().unwrap().to_string(),
        column_families_to_open: vec![ACCOUNTS_CF.to_string()],
        column_family_configs: cf_configs,
        ..Default::default()
    };
    let store = RocksDbCFOptimisticTxnStore::open(config)?;
    
    store.put(ACCOUNTS_CF, "acc_a", &Account { balance: 100, version: 1 })?;
    store.put(ACCOUNTS_CF, "acc_b", &Account { balance: 50, version: 1 })?;

    transfer(&store, "acc_a", "acc_b", 20)?;
    println!("Optimistic CF transaction succeeded.");

    let final_b: Account = store.get(ACCOUNTS_CF, "acc_b")?.unwrap();
    assert_eq!(final_b.balance, 70);
    Ok(())
}
```

---

## 6. Detailed Configuration

Configuration is distinct for non-transactional and transactional stores, and for CF-aware versus default-CF focused stores.

### Non-Transactional CF-Aware (`RocksDbCFStoreConfig`)

Used to configure a non-transactional CF-aware database (`RocksDbCFStore`).

```rust
use rocksolid::config::{
    RocksDbCFStoreConfig, BaseCfConfig, RockSolidMergeOperatorCfConfig, 
    RockSolidComparatorOpt, RecoveryMode, RockSolidCompactionFilterRouterConfig,
    CompactionFilterRouterFnPtr
};
use rocksolid::tuner::{Tunable, TuningProfile, profiles::IoCapOpts}; // Corrected path for IoCapOpts
use rocksdb::Options as RocksDbOptions;
use std::collections::HashMap;
use std::sync::Arc;

// Example:
let mut cf_configs_map = HashMap::new();
cf_configs_map.insert("user_data_cf".to_string(), BaseCfConfig {
    tuning_profile: Some(TuningProfile::LatestValue { 
        mem_budget_mb_per_cf_hint: 64, 
        use_bloom_filters: true, 
        enable_compression: true,
        io_cap: Some(IoCapOpts::default()),
     }),
    merge_operator: None, 
    comparator: None,
    compaction_filter_router: None, // Or Some(RockSolidCompactionFilterRouterConfig { ... })
});
cf_configs_map.insert(rocksdb::DEFAULT_COLUMN_FAMILY_NAME.to_string(), BaseCfConfig::default());

let cf_store_config = RocksDbCFStoreConfig {
    path: "/path/to/your_cf_db".to_string(),
    create_if_missing: true,
    column_families_to_open: vec![
        rocksdb::DEFAULT_COLUMN_FAMILY_NAME.to_string(),
        "user_data_cf".to_string(),
    ],
    column_family_configs: cf_configs_map,
    db_tuning_profile: Some(TuningProfile::MemorySaver {
        total_mem_mb: 256,
        db_block_cache_fraction: 0.5,
        db_write_buffer_manager_fraction: 0.25,
        expected_cf_count_for_write_buffers: 2,
        enable_light_compression: true,
        io_cap: None,
     }),
    parallelism: Some(num_cpus::get() as i32),
    recovery_mode: Some(RecoveryMode::PointInTime),
    enable_statistics: Some(false),
    custom_options_db_and_cf: None, // Optional: Arc<dyn Fn(...)>
};
```

**Key `RocksDbCFStoreConfig` Fields:**
*   `path: String`: Filesystem path.
*   `create_if_missing: bool`: Create DB directory if needed.
*   `column_families_to_open: Vec<String>`: Names of all CFs to open. Must include `rocksdb::DEFAULT_COLUMN_FAMILY_NAME` if it's used.
*   `column_family_configs: HashMap<String, BaseCfConfig>`: Per-CF configurations.
    *   **`BaseCfConfig` Fields:**
        *   `tuning_profile: Option<TuningProfile>`
        *   `merge_operator: Option<RockSolidMergeOperatorCfConfig>`
        *   `comparator: Option<RockSolidComparatorOpt>`
        *   `compaction_filter_router: Option<RockSolidCompactionFilterRouterConfig>`
*   `db_tuning_profile: Option<TuningProfile>`: DB-wide tuning profile.
*   `parallelism: Option<i32>`, `recovery_mode: Option<RecoveryMode>`, `enable_statistics: Option<bool>`: DB-wide hard settings that override profiles.
*   `custom_options_db_and_cf: Option<Arc<dyn Fn(&mut Tunable<RocksDbOptions>, &mut HashMap<String, Tunable<RocksDbOptions>>) + Send + Sync + 'static>>`: Advanced custom callback to modify DB and CF options after profiles are applied but before finalization.

### Non-Transactional Default-CF (`RocksDbStoreConfig`)

Simplified configuration for `RocksDbStore`. Internally, this is converted to a `RocksDbCFStoreConfig` targeting only the default CF.

```rust
use rocksolid::config::{RocksDbStoreConfig, RockSolidMergeOperatorCfConfig, RockSolidComparatorOpt, RecoveryMode};
use rocksolid::tuner::{Tunable, TuningProfile, profiles::IoCapOpts};
use rocksdb::Options as RocksDbOptions;
use std::sync::Arc;


let store_config = RocksDbStoreConfig {
    path: "/path/to/your_default_db".to_string(),
    create_if_missing: true,
    default_cf_tuning_profile: Some(TuningProfile::LatestValue { 
        mem_budget_mb_per_cf_hint: 128, 
        use_bloom_filters: true, 
        enable_compression: true,
        io_cap: None,
    }),
    default_cf_merge_operator: None, // Optional: RockSolidMergeOperatorCfConfig
    comparator: None, // Optional: RockSolidComparatorOpt for the default CF
    parallelism: Some(2),
    recovery_mode: Some(RecoveryMode::AbsoluteConsistency),
    enable_statistics: Some(false),
    custom_options_default_cf_and_db: None, // Optional: Arc<dyn Fn(&mut Tunable<RocksDbOptions>, &mut Tunable<RocksDbOptions>)>
};
```
*   `custom_options_default_cf_and_db` callback receives `Tunable<RocksDbOptions>` for DB-wide options and `Tunable<RocksDbOptions>` for the default CF's options.

### Transactional CF-Aware (`RocksDbCFTxnStoreConfig`)

Configuration for `RocksDbCFTxnStore`.

```rust
use rocksolid::tx::cf_tx_store::{RocksDbCFTxnStoreConfig, CFTxConfig, CustomDbAndCfCb};
use rocksolid::config::BaseCfConfig;
use rocksolid::tuner::Tunable;
use rocksdb::{Options as RocksDbOptions, TransactionDBOptions};
use std::collections::HashMap;

let mut cf_txn_configs_map = HashMap::new();
cf_txn_configs_map.insert("orders_cf".to_string(), CFTxConfig {
    base_config: BaseCfConfig { /* ... tuning, merge, comparator, compaction_filter ... */ },
});
cf_txn_configs_map.insert(rocksdb::DEFAULT_COLUMN_FAMILY_NAME.to_string(), CFTxConfig::default());

let my_custom_cb_txn: CustomDbAndCfCb = None; 

let cf_txn_store_config = RocksDbCFTxnStoreConfig {
    path: "/path/to/your_cf_txn_db".to_string(),
    create_if_missing: true,
    column_families_to_open: vec![
        rocksdb::DEFAULT_COLUMN_FAMILY_NAME.to_string(),
        "orders_cf".to_string(),
    ],
    column_family_configs: cf_txn_configs_map,
    db_tuning_profile: None,
    parallelism: None,
    recovery_mode: None,
    enable_statistics: None,
    custom_options_db_and_cf: my_custom_cb_txn, // Callback per CF: Fn(&str, &mut Tunable<RocksDbOptions>)
    txn_db_options: Some(TransactionDBOptions::default()),
};
```
*   Structure is similar to `RocksDbCFStoreConfig`.
*   `column_family_configs` uses `CFTxConfig` which wraps `BaseCfConfig` (allowing full CF features like compaction filters).
*   Includes `txn_db_options: Option<rocksdb::TransactionDBOptions>` for RocksDB-level transaction settings.
*   `custom_options_db_and_cf` here is `Option<Box<dyn for<'a> Fn(&'a str, &'a mut Tunable<RocksDbOptions>) + Send + Sync + 'static>>` (aliased as `CustomDbAndCfCb`). This callback is invoked for each CF specified in `column_families_to_open`, allowing CF-specific `rocksdb::Options` modification after profiles are applied.

### Transactional Default-CF (`RocksDbTxnStoreConfig`)

Simplified configuration for `RocksDbTxnStore`. Internally converted to `RocksDbCFTxnStoreConfig`.

```rust
use rocksolid::tx::tx_store::{RocksDbTxnStoreConfig, CustomDbAndDefaultCb};
use rocksolid::config::MergeOperatorConfig; // Note: Uses original MergeOperatorConfig
use rocksolid::tuner::Tunable;
use rocksdb::{Options as RocksDbOptions, TransactionDBOptions};

let my_default_custom_cb_txn: CustomDbAndDefaultCb = None;

let txn_store_config = RocksDbTxnStoreConfig {
    path: "/path/to/your_default_txn_db".to_string(),
    create_if_missing: true,
    default_cf_tuning_profile: None,
    default_cf_merge_operator: None, // Optional: Original MergeOperatorConfig for default CF
    // Note: Comparator for default CF is implicitly None here; set via custom_options or if CFTxConfig gains a comparator field.
    parallelism: None,
    recovery_mode: None,
    enable_statistics: None,
    custom_options_default_cf_and_db: my_default_custom_cb_txn, // Callback for DB-wide & default CF options
    txn_db_options: Some(TransactionDBOptions::default()),
};
```
*   Structure is similar to `RocksDbStoreConfig`.
*   Includes `txn_db_options: Option<rocksdb::TransactionDBOptions>`.
*   Uses the original `config::MergeOperatorConfig` for `default_cf_merge_operator`.
*   `custom_options_default_cf_and_db` is `Option<Box<dyn for<'a> Fn(&'a str, &'a mut Tunable<RocksDbOptions>) + Send + Sync + 'static>>` (aliased as `CustomDbAndDefaultCb`). This is called only for the default CF and DB-wide options (the `&str` argument will be `rocksdb::DEFAULT_COLUMN_FAMILY_NAME`).

### Comparators (`RockSolidComparatorOpt`)

Specify custom key comparison per CF via `BaseCfConfig.comparator` or `RocksDbStoreConfig.comparator`.
Enabled by feature flags.
```rust
use rocksolid::config::RockSolidComparatorOpt;
// Available options:
// RockSolidComparatorOpt::None (default)
// RockSolidComparatorOpt::NaturalLexicographical { ignore_case: bool } // Requires "natlex_sort" feature
// RockSolidComparatorOpt::Natural { ignore_case: bool } // Requires "nat_sort" feature, assumes UTF-8 keys
```

---

## 7. Opening & Managing Stores

Each store type (`RocksDbStore`, `RocksDbCFStore`, `RocksDbTxnStore`, `RocksDbCFTxnStore`) has:
*   `pub fn open(config: CorrespondingConfigType) -> StoreResult<Self>`
*   `pub fn destroy(path: &Path, config: CorrespondingConfigType) -> StoreResult<()>`
*   `pub fn path(&self) -> &str`

Additionally:
*   `RocksDbCFStore::db_raw() -> Arc<rocksdb::DB>`
*   `RocksDbStore::cf_store() -> Arc<RocksDbCFStore>` (Access underlying CF-aware store)
*   `RocksDbCFTxnStore::db_txn_raw() -> Arc<rocksdb::TransactionDB>`
*   `RocksDbTxnStore::cf_txn_store() -> Arc<RocksDbCFTxnStore>` (Access underlying CF-aware transactional store)

---

## 8. CF-Aware Operations (`CFOperations` Trait)

Implemented by `RocksDbCFStore` and `RocksDbCFTxnStore` (for committed reads/writes). All methods require a `cf_name: &str` argument to specify the target Column Family.

*Typical generic constraints:*
*   `K`: `rocksolid::bytes::AsBytes + std::hash::Hash + Eq + PartialEq + std::fmt::Debug`
*   `V`: `serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug` (adjust as needed per method)

### Basic CRUD & Read Operations
*   `put<K, V>(&self, cf_name: &str, key: K, value: &V) -> StoreResult<()>`
*   `get<K, V>(&self, cf_name: &str, key: K) -> StoreResult<Option<V>>`
*   `delete<K>(&self, cf_name: &str, key: K) -> StoreResult<()>`
*   `exists<K>(&self, cf_name: &str, key: K) -> StoreResult<bool>`

### Raw Byte Operations
*   `put_raw<K>(&self, cf_name: &str, key: K, raw_value: &[u8]) -> StoreResult<()>`
*   `get_raw<K>(&self, cf_name: &str, key: K) -> StoreResult<Option<Vec<u8>>>`

### Operations with Expiry
*   `put_with_expiry<K, V>(&self, cf_name: &str, key: K, value: &V, expire_time: u64) -> StoreResult<()>`
*   `get_with_expiry<K, V>(&self, cf_name: &str, key: K) -> StoreResult<Option<ValueWithExpiry<V>>>`

### Multi-Get Operations
*   `multiget<K: Clone, V>(&self, cf_name: &str, keys: &[K]) -> StoreResult<Vec<Option<V>>>`
*   `multiget_raw<K>(&self, cf_name: &str, keys: &[K]) -> StoreResult<Vec<Option<Vec<u8>>>>`
*   `multiget_with_expiry<K: Clone, V>(&self, cf_name: &str, keys: &[K]) -> StoreResult<Vec<Option<ValueWithExpiry<V>>>>`

### Merge Operations (Method Call)
*   `merge<K, PatchVal: Serialize + Debug>(&self, cf_name: &str, key: K, merge_value: &MergeValue<PatchVal>) -> StoreResult<()>`
*   `merge_raw<K>(&self, cf_name: &str, key: K, raw_merge_operand: &[u8]) -> StoreResult<()>`
*   `merge_with_expiry<K, V>(&self, cf_name: &str, key: K, value: &V, expire_time: u64) -> StoreResult<()>`

### Range Deletion
*   `delete_range<K>(&self, cf_name: &str, start_key: K, end_key: K) -> StoreResult<()>`
    *(Note: Not available on `RocksDbCFTxnStore` directly for committed data. Use transactions for ranged deletes in transactional contexts if the underlying DB transaction supports it, or perform on a non-transactional store if applicable.)*

### Unified Iteration API

The `iterate` method provides flexible data scanning across CFs.
```rust
use rocksolid::iter::{IterConfig, IterationResult, IterationMode};
use rocksolid::types::IterationControlDecision;
// Generic Signature:
// fn iterate<'store_lt, SerKey, OutK, OutV>(
//   &'store_lt self,
//   config: IterConfig<'store_lt, SerKey, OutK, OutV>,
// ) -> Result<IterationResult<'store_lt, OutK, OutV>, StoreError>
// where
//   SerKey: rocksolid::bytes::AsBytes + Hash + Eq + PartialEq + Debug,
//   OutK: serde::de::DeserializeOwned + Debug + 'store_lt,
//   OutV: serde::de::DeserializeOwned + Debug + 'store_lt;
```
*   **`IterConfig<'cfg_lt, SerKey, OutK, OutV>`**:
    *   `pub cf_name: String`: Target Column Family.
    *   `pub prefix: Option<SerKey>`: Key prefix for scanning (type `SerKey` is user's key type).
    *   `pub start: Option<SerKey>`: Start key for range scanning (type `SerKey`).
    *   `pub reverse: bool`: Iteration direction.
    *   `pub control: Option<Box<dyn FnMut(&[u8], &[u8], usize) -> IterationControlDecision + 'cfg_lt>>`: Custom control logic.
    *   `pub mode: IterationMode<'cfg_lt, OutK, OutV>`: Defines how items are processed.
    *   Constructors: `IterConfig::new_deserializing(...)`, `IterConfig::new_raw(...)`, `IterConfig::new_control_only(...)`.
*   **`IterationMode` Enum**:
    *   `Deserialize(Box<dyn FnMut(&[u8], &[u8]) -> StoreResult<(OutK, OutV)> + 'cfg_lt>)`: Deserializes raw bytes into `(OutK, OutV)`.
    *   `Raw`: Yields `(Vec<u8>, Vec<u8>)`. `OutK` and `OutV` must be `Vec<u8>`.
    *   `ControlOnly`: Only applies `control` function, no items yielded. `OutK` and `OutV` must be `()`.
*   **`IterationResult` Enum**:
    *   `DeserializedItems(Box<dyn Iterator<Item = StoreResult<(OutK, OutV)>> + 'iter_lt>)`
    *   `RawItems(Box<dyn Iterator<Item = StoreResult<(Vec<u8>, Vec<u8>)>> + 'iter_lt>)`
    *   `EffectCompleted` (for `ControlOnly` mode)
*   Strict prefix matching is enforced automatically if `IterConfig.prefix` is `Some`.

### Find Operations

Convenience methods built on top of `iterate`.
*Key type constraints typically include `bytevec::ByteDecodable` for deserializing keys from raw bytes.*
*   `find_by_prefix<Key: Clone, Val>(&self, cf_name: &str, prefix: &Key, direction: rocksdb::Direction) -> StoreResult<Vec<(Key, Val)>>`
*   `find_from<Key, Val, F>(&self, cf_name: &str, start_key: Key, direction: rocksdb::Direction, control_fn: F) -> StoreResult<Vec<(Key, Val)>>`
    *   (`Key` constraints include `AsBytes`, `ByteDecodable`, `DeserializeOwned`, etc.)
*   Similar `_with_expire_val` variants exist for finding `ValueWithExpiry<Val>` items, returning `Result<Vec<(Key, ValueWithExpiry<Val>)>, String>`.

---

## 9. Default Column Family Operations (`DefaultCFOperations` Trait)

Implemented by `RocksDbStore` and `RocksDbTxnStore`. These methods mirror the `CFOperations` API but operate implicitly on the default Column Family (i.e., they don't take a `cf_name: &str` parameter).

When using `iterate` with a type implementing `DefaultCFOperations`, ensure `IterConfig.cf_name` is set to `rocksdb::DEFAULT_COLUMN_FAMILY_NAME`.

---

## 10. Batch Operations (`BatchWriter`)

`BatchWriter` allows for non-transactional atomic write operations on a *single, specified Column Family*.
*   Obtain via `store.batch_writer(cf_name)` (for `RocksDbCFStore`) or `default_store.batch_writer()` (for `RocksDbStore`, targets default CF).
*   **Methods on `BatchWriter`**:
    *   `set<K, V>(&mut self, key: K, value: &V) -> StoreResult<&mut Self>`
    *   `set_raw<K>(&mut self, key: K, raw_value: &[u8]) -> StoreResult<&mut Self>`
    *   `set_with_expiry<K, V>(&mut self, key: K, value: &V, expire_time: u64) -> StoreResult<&mut Self>`
    *   `delete<K>(&mut self, key: K) -> StoreResult<&mut Self>`
    *   `delete_range<K>(&mut self, start_key: K, end_key: K) -> StoreResult<&mut Self>`
    *   `merge<K, PatchVal>(&mut self, key: K, merge_value: &MergeValue<PatchVal>) -> StoreResult<&mut Self>`
    *   `merge_raw<K>(&mut self, key: K, raw_merge_operand: &[u8]) -> StoreResult<&mut Self>`
*   **Multi-CF Atomic Batches**:
    *   Use `raw_batch_mut() -> StoreResult<&mut rocksdb::WriteBatch>` to get the underlying `WriteBatch`.
    *   Manually add CF-specific operations using CF handles obtained from the store (e.g., `store.get_cf_handle(cf_name)?`). This provides an escape hatch for atomic writes across multiple CFs if true transactions are not used or desired.
*   **Finalization**:
    *   `commit(self) -> StoreResult<()>`: Applies the batch. Consumes the writer.
    *   `discard(self)`: Discards the batch. Consumes the writer.
    *   RAII: If dropped without `commit` or `discard`, a warning is logged, and operations are *not* applied.

---

## 11. Transactional Operations

### `RocksDbCFTxnStore` (CF-Aware Pessimistic Transactions)
*   Implements `CFOperations` for operations on *committed* data (these are auto-committed).
*   **Explicit Pessimistic Transactions**:
    *   `begin_transaction(&self, ...) -> Tx<'_>`: Starts a new transaction. `Tx<'_>` is an alias for `rocksdb::Transaction<'_, rocksdb::TransactionDB>`.
    *   `execute_transaction<F, R>(&self, ..., operation: F) -> StoreResult<R>`:
        *   Executes the given closure `operation` within a transaction.
        *   The closure receives `&Tx` and should return `StoreResult<R>`.
        *   Automatically commits if `Ok(R)` is returned, or rolls back if `Err(StoreError)` is returned.
    *   **Within the transaction closure (or with a `Tx` object)**, use CF-aware transactional methods of `RocksDbCFTxnStore`:
        *   `get_in_txn<K, V>(&self, txn: &Tx, cf_name: &str, key: K) -> StoreResult<Option<V>>`
        *   `put_in_txn_cf<K, V>(&self, txn: &Tx, cf_name: &str, key: K, value: &V) -> StoreResult<()>`
        *   And similar `_raw`, `_with_expiry`, `exists_in_txn`, `delete_in_txn_cf`, `merge_in_txn_cf` variants.

### `RocksDbTxnStore` (Default-CF Focused Pessimistic Transactions)
*   Implements `DefaultCFOperations` for operations on *committed* data on the default CF.
*   **Explicit Pessimistic Transactions**:
    *   `transaction_context(&self) -> TransactionContext<'_>`:
        *   Provides a convenient RAII wrapper for a transaction on the default CF.
        *   Methods on `TransactionContext` (`set`, `get`, `delete`, etc.) operate on the default CF within the transaction.
        *   `ctx.commit()?` or `ctx.rollback()?` finalize. Rolls back on drop if not completed.
    *   `begin_transaction(...) -> Tx<'_>`: Get a raw `Tx` object for more direct control.
    *   `execute_transaction(...)`: Same as for `RocksDbCFTxnStore`, but typically used with operations on the default CF.

### `RocksDbCFOptimisticTxnStore` (CF-Aware Optimistic Transactions)
*   Implements `CFOperations` for operations on *committed* data.
*   **Recommended: Automatic Retry Builder**:
    *   `optimistic_transaction(&self) -> OptimisticTransactionBuilder<FixedRetry>`: Returns a builder to construct and execute a transaction with automatic conflict detection and retries. This is the preferred method.
    *   **Builder Methods**:
        *   `.with_retry_policy(policy: impl RetryPolicy)`: Use a custom retry strategy (e.g., `NoRetry`, or your own implementation).
        *   `.execute_with_snapshot(operation: F) -> StoreResult<R>`: Executes the closure `operation`. The closure receives a `&rocksdb::Transaction<'_, OptimisticTransactionDB>` which provides a consistent point-in-time view for reads. If a commit fails due to a conflict, the entire operation is retried according to the policy. This is the safest and most common method.
        *   `.execute_unisolated(operation: F) -> StoreResult<R>`: Executes the closure without snapshot protection. Reads are not guaranteed to be consistent. Faster but less safe.
*   **Manual Transaction Management**:
    *   `transaction_context(&self) -> OptimisticTransactionContext<'_>`: Creates a context for a single transaction attempt with conflict detection enabled.
    *   `blind_transaction_context(&self) -> OptimisticTransactionContext<'_>`: Creates a context with conflict detection disabled ("last write wins").
    *   `OptimisticTransactionContext` methods (`set`, `get`, `commit`, `rollback`, etc.) operate on a single attempt. If `.commit()` fails with a conflict error, the **application is responsible for creating a new context and retrying the entire operation**.

### `RocksDbOptimisticTxnStore` (Default-CF Focused Optimistic Transactions)
*   Implements `DefaultCFOperations` for operations on *committed* data on the default CF.
*   Provides the same `optimistic_transaction()` builder and manual `transaction_context()` / `blind_transaction_context()` methods as its CF-aware counterpart, but all operations within the transaction contexts (`set`, `get`, etc.) target the default CF.

---

## 12. Merge Operator Configuration & Usage

Merge operators allow custom logic for combining multiple write operations (merges) for the same key.
1.  **Define Merge Logic**:
    *   Implement `MergeFn`: `fn(new_key: &[u8], existing_val: Option<&[u8]>, operands: &rocksdb::MergeOperands) -> Option<Vec<u8>>`. This function defines how to combine an existing value (if any) with a series of merge operands.
    *   **OR** use `rocksolid::merge::MergeRouterBuilder` for key-pattern based routing:
        *   `MergeRouterBuilder::new()`
        *   `.operator_name("MyRouterName")`
        *   `.add_full_merge_route("/items/:id/counter", item_counter_merge_fn)?`
        *   `.add_partial_merge_route(...)` (optional, if partial merge is different)
        *   `.add_route(pattern, full_fn, partial_fn)?`
        *   `.build() -> StoreResult<MergeOperatorConfig>` (Note: `MergeOperatorConfig` is used by `RocksDbTxnStoreConfig`, `RockSolidMergeOperatorCfConfig` by others. Ensure type compatibility or conversion).
        *   ⚠️ **Router Warning**: Uses globally shared static routers. Ensure unique key patterns/prefixes if using the same routed merge operator name across different CFs/DBs.
2.  **Configure**:
    *   For `RocksDbCFStoreConfig` / `BaseCfConfig`: Use `RockSolidMergeOperatorCfConfig { name, full_merge_fn, partial_merge_fn }` in `BaseCfConfig.merge_operator`.
    *   For `RocksDbStoreConfig` / `RocksDbTxnStoreConfig` (default CF): Use `MergeOperatorConfig { name, full_merge_fn, partial_merge_fn }` in `default_cf_merge_operator`.
3.  **Apply to Store Configuration**.
4.  **Use Merge Methods**:
    *   `store.merge<K, PatchVal>(cf_name, key, &MergeValue(operator, patch_value))?`
    *   `store.merge_in_txn_cf<K, PatchVal>(txn, cf_name, key, &MergeValue(operator, patch_value))?`
    *   `batch_writer.merge(...)`
    *   `transaction_context.merge(...)`
    *   `MergeValueOperator` enum: `Add`, `Remove`, `Union`, `Intersect`.

---

## 13. Compaction Filter Configuration & Usage

Compaction filters allow custom logic to be applied to key-value pairs during RocksDB's background compaction process. This can be used to modify, remove, or keep data.
1.  **Define Filter Logic**:
    *   Implement `CompactionFilterRouteHandlerFn`:
        `Arc<dyn Fn(level: u32, key: &[u8], value: &[u8], params: &matchit::Params) -> rocksdb::compaction_filter::Decision + Send + Sync + 'static>`
    *   The handler returns `rocksdb::compaction_filter::Decision` (`Keep`, `Remove`, `ChangeValue(Vec<u8>)`, `RemoveAndSkipUntil(Vec<u8>)`).
2.  **Build Router Configuration**: Use `rocksolid::compaction_filter::CompactionFilterRouterBuilder`.
    *   `CompactionFilterRouterBuilder::new()`
    *   `.operator_name("MyCompactionRouter")`
    *   `.add_route("/cache_entries/*", cache_expiry_filter_fn)?`
    *   `.add_route("/logs/old/:year", old_log_archiver_fn)?`
    *   `.build() -> StoreResult<RockSolidCompactionFilterRouterConfig>`
    *   ⚠️ **Router Warning**: Uses globally shared static routers. Ensure unique key patterns if using the same routed operator name across different CFs/DBs.
3.  **Configure**:
    *   Set the `RockSolidCompactionFilterRouterConfig` instance to `BaseCfConfig.compaction_filter_router`. This applies the filter to a specific Column Family.
4.  **Operation**:
    *   The filter logic will be invoked automatically by RocksDB during compaction.
    *   You can manually trigger compaction for a CF using `store.db_raw().compact_range_cf(&cf_handle, None, None)?;` (ensure data is flushed to SSTs first using `flush_cf`).
    *   Useful for implementing TTL (Time-To-Live) by checking `ValueWithExpiry` in a filter, data scrubbing, or schema evolution.

---

## 14. Helper Macros (`macros.rs`)

`rocksolid` provides macros to reduce boilerplate for common DAO (Data Access Object) patterns.
*   **Default CF Macros** (e.g., `generate_dao_get!`, `generate_dao_put!`, `generate_dao_multiget!`):
    *   Designed for stores implementing `DefaultCFOperations` (like `RocksDbStore`).
    *   Example: `fn get_user(&self, user_id: &str) -> StoreResult<Option<User>> { generate_dao_get!(self.store, user_id_to_key_fn(user_id)) }`
*   **CF-Aware Macros** (e.g., `generate_dao_get_cf!`, `generate_dao_put_cf!`, `generate_dao_multiget_cf!`):
    *   Designed for stores implementing `CFOperations` (like `RocksDbCFStore`).
    *   Require `cf_name` as an argument.
    *   Example: `fn get_product(&self, cf: &str, sku: &str) -> StoreResult<Option<Product>> { generate_dao_get_cf!(self.store, cf, sku) }`
*   **Transactional Macros for Default CF**:
    *   `generate_dao_merge_in_txn!`, `generate_dao_remove_in_txn!`: Work with a `&Tx` object.
    *   **Important for Puts in Transactions**:
        *   `generate_dao_put_in_txn!` and `generate_dao_put_with_expiry_in_txn!` macros currently rely on static helper functions (`rocksolid::tx::put_in_txn` etc.) that are **not directly provided in the current `rocksolid::tx` module for `Tx` objects**.
        *   For putting data within a transaction:
            *   Use `TransactionContext::set()` or `TransactionContext::set_with_expiry()`.
            *   Or, directly use `txn.put(...)` / `txn.put_cf(...)` methods on the `Tx` object.
*   **No CF-aware transactional macros** are currently provided. Use `RocksDbCFTxnStore` methods like `put_in_txn_cf` directly with a `&Tx` object.

---

## 15. Utilities (`utils.rs`)

*   `backup_db(backup_path: &Path, cfg_to_open_db: RocksDbCFStoreConfig) -> StoreResult<()>`:
    Creates a RocksDB checkpoint (live backup) of the database specified by `cfg_to_open_db` at `backup_path`.
*   `migrate_db(src_config: RocksDbCFStoreConfig, dst_config: RocksDbCFStoreConfig, validate: bool) -> StoreResult<()>`:
    Migrates data from a source DB to a destination DB, including all configured Column Families. If `validate` is true, it performs a key-by-key comparison after migration (can be slow).

---

## 16. Error Handling (`StoreError`, `StoreResult`)

*   Most operations in `rocksolid` return `StoreResult<T>`, which is an alias for `Result<T, StoreError>`.
*   **`StoreError` Enum Variants**:
    *   `RocksDb(rocksdb::Error)`: Underlying RocksDB error.
    *   `Serialization(String)`: Error during value serialization (e.g., with `serde`).
    *   `Deserialization(String)`: Error during value deserialization.
    *   `KeyEncoding(String)`: Error during key serialization (less common with `AsBytes`).
    *   `KeyDecoding(String)`: Error during key deserialization (e.g., with `ByteDecodable`).
    *   `InvalidConfiguration(String)`: Problem with store or feature configuration.
    *   `TransactionRequired`: Operation attempted outside a necessary transaction.
    *   `Io(std::io::Error)`: Filesystem I/O error.
    *   `NotFound { key: Option<Vec<u8>> }`: Key not found (often handled gracefully by methods returning `Option`).
    *   `MergeError(String)`: Specific error during a merge operation.
    *   `UnknownCf(String)`: Specified Column Family not found or not opened.
    *   `Other(String)`: For miscellaneous library-specific errors.
*   The `StoreErrorExt` trait provides helpers like `map_to_option` and `map_to_vec` on `StoreResult` to easily convert `NotFound` errors into `Ok(None)` or `Ok(vec![])`.