### Build Grocksdb with Custom CFLAGS and LDFLAGS Source: https://github.com/linxgnu/grocksdb/blob/master/README.md Use these commands to build your application after installing rocksdb and grocksdb. Adjust paths and libraries as needed for your system. ```bash CGO_CFLAGS="-I/path/to/rocksdb/include" \ CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd" \ go build ``` ```bash go build // if prerequisites are in linker paths ``` ```bash CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lm -lz -lsnappy -llz4 -lzstd -lbz2" \ go build ``` -------------------------------- ### Manage GrocksDB Data with Column Families Source: https://context7.com/linxgnu/grocksdb/llms.txt Utilize column families to logically partition data within a single database. Each column family has independent configuration options. This example demonstrates opening a database with multiple column families, writing to them, and iterating over a specific one. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) opts.SetCreateIfMissingColumnFamilies(true) // Define column families with their options cfNames := []string{"default", "users", "orders"} cfOpts := make([]*grocksdb.Options, len(cfNames)) for i := range cfOpts { cfOpts[i] = grocksdb.NewDefaultOptions() } // Open database with column families db, cfHandles, err := grocksdb.OpenDbColumnFamilies(opts, "/path/to/db", cfNames, cfOpts) if err != nil { log.Fatal(err) } defer db.Close() defer func() { for _, cf := range cfHandles { cf.Destroy() } }() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() // Write to specific column families usersCF := cfHandles[1] // "users" column family ordersCF := cfHandles[2] // "orders" column family db.PutCF(wo, usersCF, []byte("user:1"), []byte(`{"name":"Alice"}`)) db.PutCF(wo, usersCF, []byte("user:2"), []byte(`{"name":"Bob"}`)) db.PutCF(wo, ordersCF, []byte("order:100"), []byte(`{"total":99.99}`)) // Read from column families value, err := db.GetCF(ro, usersCF, []byte("user:1")) if err != nil { log.Fatal(err) } defer value.Free() fmt.Printf("User 1: %s\n", value.Data()) // Output: User 1: {"name":"Alice"} // Iterate over a column family fmt.Println("\nAll orders:") it := db.NewIteratorCF(ro, ordersCF) defer it.Close() for it.SeekToFirst(); it.Valid(); it.Next() { key := it.Key() val := it.Value() fmt.Printf(" %s: %s\n", key.Data(), val.Data()) key.Free() val.Free() } wo.Destroy() ro.Destroy() } ``` -------------------------------- ### Basic Put, Get, and Delete Operations in RocksDB Source: https://context7.com/linxgnu/grocksdb/llms.txt Performs fundamental key-value operations. Remember to free the returned Slice after use to prevent memory leaks. Write options control sync behavior, and read options control snapshot and cache settings. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() // Put a key-value pair err = db.Put(wo, []byte("hello"), []byte("world")) if err != nil { log.Fatal(err) } // Get the value value, err := db.Get(ro, []byte("hello")) if err != nil { log.Fatal(err) } defer value.Free() // Important: free the slice if value.Exists() { fmt.Printf("Value: %s\n", string(value.Data())) // Output: Value: world } // Delete the key err = db.Delete(wo, []byte("hello")) if err != nil { log.Fatal(err) } // Verify deletion value2, _ := db.Get(ro, []byte("hello")) defer value2.Free() fmt.Printf("Exists after delete: %v\n", value2.Exists()) // Output: Exists after delete: false wo.Destroy() ro.Destroy() } ``` -------------------------------- ### Create and Use Database Snapshot in Go Source: https://context7.com/linxgnu/grocksdb/llms.txt Demonstrates creating a snapshot to read data as it existed at a specific point in time, unaffected by subsequent writes. Ensure to release the snapshot when done. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() // Write initial data db.Put(wo, []byte("counter"), []byte("100")) // Create a snapshot snapshot := db.NewSnapshot() fmt.Printf("Snapshot sequence number: %d\n", snapshot.GetSequenceNumber()) // Modify data after snapshot db.Put(wo, []byte("counter"), []byte("200")) db.Put(wo, []byte("new_key"), []byte("new_value")) // Read current value current, _ := db.Get(ro, []byte("counter")) fmt.Printf("Current value: %s\n", current.Data()) current.Free() // Output: Current value: 200 // Read from snapshot (point-in-time view) roSnapshot := grocksdb.NewDefaultReadOptions() roSnapshot.SetSnapshot(snapshot) snapshotValue, _ := db.Get(roSnapshot, []byte("counter")) fmt.Printf("Snapshot value: %s\n", snapshotValue.Data()) snapshotValue.Free() // Output: Snapshot value: 100 // Key added after snapshot won't exist in snapshot view newKey, _ := db.Get(roSnapshot, []byte("new_key")) fmt.Printf("new_key exists in snapshot: %v\n", newKey.Exists()) newKey.Free() // Output: new_key exists in snapshot: false // Release snapshot when done db.ReleaseSnapshot(snapshot) wo.Destroy() ro.Destroy() roSnapshot.Destroy() } ``` -------------------------------- ### Backup and Restore Operations in Go Source: https://context7.com/linxgnu/grocksdb/llms.txt Demonstrates creating, listing, verifying, and restoring database backups using grocksdb. Supports incremental backups and purging old backups. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } wo := grocksdb.NewDefaultWriteOptions() db.Put(wo, []byte("key1"), []byte("value1")) db.Put(wo, []byte("key2"), []byte("value2")) // Create backup engine backupEngine, err := grocksdb.CreateBackupEngineWithPath(db, "/path/to/backups") if err != nil { log.Fatal(err) } // Create a new backup (flush before backup) err = backupEngine.CreateNewBackupFlush(true) if err != nil { log.Fatal(err) } fmt.Println("Backup created successfully") // List all backups backups := backupEngine.GetInfo() for _, info := range backups { fmt.Printf("Backup ID: %d, Size: %d bytes, Files: %d\n", info.ID, info.Size, info.NumFiles) } // Verify backup if len(backups) > 0 { err = backupEngine.VerifyBackup(backups[0].ID) if err != nil { log.Fatal(err) } fmt.Println("Backup verified successfully") } backupEngine.Close() db.Close() // Restore from backup backupOpts := grocksdb.NewDefaultOptions() restoreEngine, err := grocksdb.OpenBackupEngine(backupOpts, "/path/to/backups") if err != nil { log.Fatal(err) } restoreOpts := grocksdb.NewRestoreOptions() err = restoreEngine.RestoreDBFromLatestBackup("/path/to/restored_db", "/path/to/restored_db", restoreOpts) if err != nil { log.Fatal(err) } fmt.Println("Database restored successfully") // Purge old backups, keeping only the latest 3 restoreEngine.PurgeOldBackups(3) restoreEngine.Close() restoreOpts.Destroy() backupOpts.Destroy() wo.Destroy() } ``` -------------------------------- ### Open Database Read-Only and as Secondary Instance in Go Source: https://context7.com/linxgnu/grocksdb/llms.txt Demonstrates opening a grocksdb database in read-only mode and as a secondary instance. Ensure to close the database and free resources when done. ```go package main import ( "fmt" "log" "time" "github.com/linxGnu/grocksdb" ) func main() { // Open as read-only (no writes allowed) opts := grocksdb.NewDefaultOptions() roDb, err := grocksdb.OpenDbForReadOnly(opts, "/path/to/primary_db", false) if err != nil { log.Fatal(err) } defer roDb.Close() ro := grocksdb.NewDefaultReadOptions() value, _ := roDb.Get(ro, []byte("key")) if value.Exists() { fmt.Printf("Read-only value: %s\n", value.Data()) } value.Free() // Open as secondary instance (follows primary) secondaryOpts := grocksdb.NewDefaultOptions() secondaryDb, err := grocksdb.OpenDbAsSecondary( secondaryOpts, "/path/to/primary_db", "/path/to/secondary_logs", ) if err != nil { log.Fatal(err) } defer secondaryDb.Close() // Catch up with primary periodically ticker := time.NewTicker(5 * time.Second) go func() { for range ticker.C { err := secondaryDb.TryCatchUpWithPrimary() if err != nil { log.Printf("Catch up error: %v", err) } } }() // Read from secondary secValue, _ := secondaryDb.Get(ro, []byte("key")) if secValue.Exists() { fmt.Printf("Secondary value: %s\n", secValue.Data()) } secValue.Free() ro.Destroy() } ``` -------------------------------- ### Open RocksDB Database with Options Source: https://context7.com/linxgnu/grocksdb/llms.txt Opens a RocksDB database with custom options, including block-based table options with LRU cache and compression. The database is created if it doesn't exist when `SetCreateIfMissing(true)` is enabled. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { // Create block-based table options with LRU cache bbto := grocksdb.NewDefaultBlockBasedTableOptions() bbto.SetBlockCache(grocksdb.NewLRUCache(3 << 30)) // 3GB cache // Create database options opts := grocksdb.NewDefaultOptions() opts.SetBlockBasedTableFactory(bbto) opts.SetCreateIfMissing(true) opts.SetCompression(grocksdb.LZ4Compression) // Open the database db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatalf("Failed to open database: %v", err) } defer db.Close() fmt.Println("Database opened successfully:", db.Name()) } ``` -------------------------------- ### MultiGet for Batch Reads in Go Source: https://context7.com/linxgnu/grocksdb/llms.txt Efficiently retrieves multiple keys in a single call. Remember to free each returned slice after use. Handles non-existent keys gracefully. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() // Prepare data db.Put(wo, []byte("product:1"), []byte("Laptop")) db.Put(wo, []byte("product:2"), []byte("Phone")) db.Put(wo, []byte("product:3"), []byte("Tablet")) // Fetch multiple keys at once keys := [][]byte{ []byte("product:1"), []byte("product:2"), []byte("product:3"), []byte("product:999"), // Non-existent key } values, err := db.MultiGet(ro, keys...) if err != nil { log.Fatal(err) } // Process results for i, value := range values { if value.Exists() { fmt.Printf("%s = %s\n", keys[i], value.Data()) } else { fmt.Printf("%s = (not found)\n", keys[i]) } value.Free() // Free each slice } // Output: // product:1 = Laptop // product:2 = Phone // product:3 = Tablet // product:999 = (not found) wo.Destroy() ro.Destroy() } ``` -------------------------------- ### Perform ACID Transactions in Go with TransactionDB Source: https://context7.com/linxgnu/grocksdb/llms.txt Illustrates how to use TransactionDB for ACID-compliant transactions, including reading data with exclusive locks (GetForUpdate), modifying data within a transaction, and committing or rolling back changes. Ensure to destroy transaction-related options and the transaction itself. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) txnDbOpts := grocksdb.NewDefaultTransactionDBOptions() db, err := grocksdb.OpenTransactionDb(opts, txnDbOpts, "/path/to/txndb") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() txnOpts := grocksdb.NewDefaultTransactionOptions() // Initialize account balances db.Put(wo, []byte("account:alice"), []byte("1000")) db.Put(wo, []byte("account:bob"), []byte("500")) // Begin a transaction for money transfer txn := db.TransactionBegin(wo, txnOpts, nil) // Read balances with exclusive locks (GetForUpdate) aliceBalance, err := txn.GetForUpdate(ro, []byte("account:alice")) if err != nil { txn.Rollback() log.Fatal(err) } fmt.Printf("Alice balance: %s\n", aliceBalance.Data()) bobBalance, err := txn.GetForUpdate(ro, []byte("account:bob")) if err != nil { txn.Rollback() log.Fatal(err) } fmt.Printf("Bob balance: %s\n", bobBalance.Data()) // Perform transfer: Alice -> Bob $100 txn.Put([]byte("account:alice"), []byte("900")) txn.Put([]byte("account:bob"), []byte("600")) // Commit the transaction err = txn.Commit() if err != nil { txn.Rollback() log.Fatal(err) } fmt.Println("Transaction committed successfully") // Verify new balances newAlice, _ := db.Get(ro, []byte("account:alice")) newBob, _ := db.Get(ro, []byte("account:bob")) fmt.Printf("New balances - Alice: %s, Bob: %s\n", newAlice.Data(), newBob.Data()) // Output: New balances - Alice: 900, Bob: 600 aliceBalance.Free() bobBalance.Free() newAlice.Free() newBob.Free() txn.Destroy() wo.Destroy() ro.Destroy() txnOpts.Destroy() txnDbOpts.Destroy() } ``` -------------------------------- ### Create SST File for Bulk Loading Source: https://context7.com/linxgnu/grocksdb/llms.txt Generates an SST file externally for bulk data import. Ensure keys are added in sorted order. This method is efficient for initial data population or large dataset migrations. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { envOpts := grocksdb.NewDefaultEnvOptions() dbOpts := grocksdb.NewDefaultOptions() // Create SST file writer writer := grocksdb.NewSSTFileWriter(envOpts, dbOpts) // Open SST file for writing err := writer.Open("/tmp/data.sst") if err != nil { log.Fatal(err) } // Add key-value pairs (MUST be in sorted order) for i := 0; i < 10000; i++ { key := fmt.Sprintf("key_%08d", i) value := fmt.Sprintf("value_%d", i) err = writer.Put([]byte(key), []byte(value)) if err != nil { log.Fatal(err) } } fmt.Printf("SST file size: %d bytes\n", writer.FileSize()) // Finish writing and close file err = writer.Finish() if err != nil { log.Fatal(err) } writer.Destroy() // Now ingest the SST file into a database opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() ingestOpts := grocksdb.NewDefaultIngestExternalFileOptions() err = db.IngestExternalFile([]string{"/tmp/data.sst"}, ingestOpts) if err != nil { log.Fatal(err) } fmt.Println("SST file ingested successfully") // Verify data ro := grocksdb.NewDefaultReadOptions() value, _ := db.Get(ro, []byte("key_00000001")) fmt.Printf("key_00000001 = %s\n", value.Data()) value.Free() ingestOpts.Destroy() ro.Destroy() envOpts.Destroy() dbOpts.Destroy() } ``` -------------------------------- ### Bloom Filters for Read Optimization in Go Source: https://context7.com/linxgnu/grocksdb/llms.txt Configures grocksdb with bloom filters to reduce disk reads for non-existent keys. Essential for workloads with many negative lookups. Requires setting block-based table options and optimizing for point lookups. ```go package main import ( "log" "github.com/linxGnu/grocksdb" ) func main() { // Create bloom filter (10 bits per key) filter := grocksdb.NewBloomFilter(10) // Configure block-based table with bloom filter bbto := grocksdb.NewDefaultBlockBasedTableOptions() bbto.SetFilterPolicy(filter) bbto.SetBlockCache(grocksdb.NewLRUCache(512 << 20)) // 512MB cache bbto.SetCacheIndexAndFilterBlocks(true) opts := grocksdb.NewDefaultOptions() opts.SetBlockBasedTableFactory(bbto) opts.SetCreateIfMissing(true) opts.SetCompression(grocksdb.LZ4Compression) // Optimize for point lookups (uses bloom filter internally) opts.OptimizeForPointLookup(64) // 64MB block cache opts.SetAllowConcurrentMemtableWrites(false) // Required with OptimizeForPointLookup db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() // Bloom filter automatically optimizes Get operations // Negative lookups (non-existent keys) will be much faster } ``` -------------------------------- ### Perform Compaction and Maintenance Source: https://context7.com/linxgnu/grocksdb/llms.txt Manually triggers database compaction for specified ranges or the entire database. Also demonstrates retrieving database statistics and live file metadata, and flushing memtables. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) opts.SetCompactionStyle(grocksdb.LevelCompactionStyle) opts.SetLevel0FileNumCompactionTrigger(4) opts.SetMaxBackgroundJobs(4) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() // Trigger manual compaction on entire database db.CompactRange(grocksdb.Range{Start: nil, Limit: nil}) // Compact a specific range db.CompactRange(grocksdb.Range{ Start: []byte("a"), Limit: []byte("z"), }) // Get database property stats := db.GetProperty("rocksdb.stats") fmt.Println("Database stats:", stats[:200], "...") // Get specific numeric property numFiles, ok := db.GetIntProperty("rocksdb.num-files-at-level0") if ok { fmt.Printf("Files at level 0: %d\n", numFiles) } // Get live file metadata liveFiles := db.GetLiveFilesMetaData() for _, f := range liveFiles { fmt.Printf("SST file: %s, Level: %d, Size: %d bytes\n", f.Name, f.Level, f.Size) } // Flush memtable to disk flushOpts := grocksdb.NewDefaultFlushOptions() flushOpts.SetWait(true) db.Flush(flushOpts) flushOpts.Destroy() fmt.Println("Maintenance operations completed") } ``` -------------------------------- ### Atomic Batch Writes with WriteBatch Source: https://context7.com/linxgnu/grocksdb/llms.txt Groups Put, Delete, and Merge operations into a single atomic WriteBatch for efficient bulk operations. The batch is executed atomically against the database. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() // Create a write batch wb := grocksdb.NewWriteBatch() defer wb.Destroy() // Add multiple operations to the batch wb.Put([]byte("key1"), []byte("value1")) wb.Put([]byte("key2"), []byte("value2")) wb.Put([]byte("key3"), []byte("value3")) wb.Delete([]byte("old_key")) fmt.Printf("Batch contains %d operations\n", wb.Count()) // Output: Batch contains 4 operations // Execute the batch atomically err = db.Write(wo, wb) if err != nil { log.Fatal(err) } // Verify writes for _, key := range []string{"key1", "key2", "key3"} { value, _ := db.Get(ro, []byte(key)) fmt.Printf("%s = %s\n", key, string(value.Data())) value.Free() } wo.Destroy() ro.Destroy() } ``` -------------------------------- ### Customize Build Flags with grocksdb_clean_link Source: https://github.com/linxgnu/grocksdb/blob/master/README.md Use the grocksdb_clean_link tag to create a cleaner set of build flags. This allows for more control over the linked libraries. ```bash CGO_LDFLAGS="-L/path/to/rocksdb -lzstd" go build -tags grocksdb_clean_link ``` -------------------------------- ### Iterate GrocksDB Keys Forward and Backward Source: https://context7.com/linxgnu/grocksdb/llms.txt Use `NewIterator` to create an iterator for forward and backward traversal. Supports seeking to specific keys and prefix scanning. Always check `Valid()` and `Err()` during iteration. ```go package main import ( "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() // Insert sample data for i := 0; i < 10; i++ { key := fmt.Sprintf("user:%03d", i) value := fmt.Sprintf("data_%d", i) db.Put(wo, []byte(key), []byte(value)) } ro := grocksdb.NewDefaultReadOptions() ro.SetFillCache(false) // Don't disturb cache during bulk reads it := db.NewIterator(ro) defer it.Close() // Iterate from a specific key fmt.Println("Forward iteration from user:005:") it.Seek([]byte("user:005")) for ; it.Valid(); it.Next() { key := it.Key() value := it.Value() fmt.Printf(" %s -> %s\n", key.Data(), value.Data()) key.Free() value.Free() } // Check for iteration errors if err := it.Err(); err != nil { log.Fatal(err) } // Iterate with prefix fmt.Println("\nPrefix iteration (user:00):") prefix := []byte("user:00") it.Seek(prefix) for ; it.ValidForPrefix(prefix); it.Next() { key := it.Key() value := it.Value() fmt.Printf(" %s -> %s\n", key.Data(), value.Data()) key.Free() value.Free() } wo.Destroy() ro.Destroy() } ``` -------------------------------- ### Implement Counter with Merge Operator Source: https://context7.com/linxgnu/grocksdb/llms.txt Utilizes the built-in uint64 add merge operator for efficient counter increments without reading the current value. Requires setting the merge operator on DB options. ```go package main import ( "encoding/binary" "fmt" "log" "github.com/linxGnu/grocksdb" ) func main() { opts := grocksdb.NewDefaultOptions() opts.SetCreateIfMissing(true) // Use built-in uint64 add merge operator opts.SetUint64AddMergeOperator() db, err := grocksdb.OpenDb(opts, "/path/to/db") if err != nil { log.Fatal(err) } defer db.Close() wo := grocksdb.NewDefaultWriteOptions() ro := grocksdb.NewDefaultReadOptions() key := []byte("counter") // Helper to encode uint64 encode := func(v uint64) []byte { b := make([]byte, 8) binary.LittleEndian.PutUint64(b, v) return b } // Helper to decode uint64 decode := func(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } // Initialize counter db.Put(wo, key, encode(100)) // Merge (add) values - no read required! db.Merge(wo, key, encode(10)) // 100 + 10 = 110 db.Merge(wo, key, encode(5)) // 110 + 5 = 115 db.Merge(wo, key, encode(25)) // 115 + 25 = 140 // Read final value value, _ := db.Get(ro, key) result := decode(value.Data()) fmt.Printf("Counter value: %d\n", result) // Output: Counter value: 140 value.Free() wo.Destroy() ro.Destroy() } ``` -------------------------------- ### Customize Build Flags with grocksdb_no_link Source: https://github.com/linxgnu/grocksdb/blob/master/README.md Use the grocksdb_no_link tag to ignore the library's default build flags and rely entirely on custom flags for building. ```bash CGO_LDFLAGS="-L/path/to/rocksdb -lrocksdb -lstdc++ -lzstd -llz4" go build -tags grocksdb_clean_link ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.