|
| 1 | + |
| 2 | +# Getting Started with Key-Value-Storage (persistency) |
| 3 | + |
| 4 | +This guide will help you get started with the C++ and Rust implementations of the Key-Value-Storage (KVS) library, including how to use it and link it with Bazel. |
| 5 | + |
| 6 | +## 1. Integrating with Bazel |
| 7 | + |
| 8 | +### 1.1 Add this to your MODULE.bazel: |
| 9 | +<details> |
| 10 | + <summary>MODULE.bazel</summary> |
| 11 | + |
| 12 | + module(name = "your_project_name") |
| 13 | + |
| 14 | + # Add the persistency dependency (replace version as needed) |
| 15 | + bazel_dep(name = "persistency", version = "0.2.0") |
| 16 | + |
| 17 | + # Add required toolchains and dependencies for C++ and Rust |
| 18 | + bazel_dep(name = "score_toolchains_gcc", version = "0.4", dev_dependency=True) |
| 19 | + gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency=True) |
| 20 | + gcc.toolchain( |
| 21 | + url = "https://github.com/eclipse-score/toolchains_gcc_packages/releases/download/0.0.1/x86_64-unknown-linux-gnu_gcc12.tar.gz", |
| 22 | + sha256 = "457f5f20f57528033cb840d708b507050d711ae93e009388847e113b11bf3600", |
| 23 | + strip_prefix = "x86_64-unknown-linux-gnu", |
| 24 | + ) |
| 25 | + use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") |
| 26 | + |
| 27 | + bazel_dep(name = "rules_rust", version = "0.61.0") |
| 28 | + crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate") |
| 29 | + crate.from_specs(name = "crate_index") |
| 30 | + use_repo(crate, "crate_index") |
| 31 | + |
| 32 | + # Add any other dependencies required by persistency (see persistency's own MODULE.bazel for details) |
| 33 | + |
| 34 | +</details> |
| 35 | + |
| 36 | +### 1.2 Insert this into your .bazelrc: |
| 37 | +<details> |
| 38 | + <summary>.bazelrc</summary> |
| 39 | + |
| 40 | + ``` |
| 41 | + build --@score_baselibs//score/json:base_library=nlohmann |
| 42 | + build --@score_baselibs//score/mw/log/flags:KRemote_Logging=False |
| 43 | +
|
| 44 | + ``` |
| 45 | +</details> |
| 46 | + |
| 47 | +### 1.3 Run Bazel |
| 48 | +If you start with a plain project add an empty file called `BUILD` into your project folder. |
| 49 | + |
| 50 | +Now you can build the project with the command: |
| 51 | +```sh |
| 52 | +bazel build //... |
| 53 | +``` |
| 54 | +(So far nothing happens, because no targets were defined.) |
| 55 | + |
| 56 | +You can now continue in this guide to create a simple consumer-producer program or start on your own. |
| 57 | + |
| 58 | + |
| 59 | + |
| 60 | +## 2. Using the C++ Implementation |
| 61 | + |
| 62 | +### 2.1 Basic Usage |
| 63 | + |
| 64 | +The C++ API is centered around `KvsBuilder` and `Kvs` classes. Here is a minimal example based on the test scenarios: |
| 65 | + |
| 66 | +```cpp |
| 67 | +#include "kvsbuilder.hpp" |
| 68 | +#include <iostream> |
| 69 | + |
| 70 | +int main() { |
| 71 | + // Use fully qualified names instead of 'using namespace' |
| 72 | + auto open_res = score::mw::per::kvs::KvsBuilder(score::mw::per::kvs::InstanceId(0)) |
| 73 | + .need_defaults_flag(true) |
| 74 | + .need_kvs_flag(false) |
| 75 | + .dir(".") |
| 76 | + .build(); |
| 77 | + if (!open_res) { |
| 78 | + std::cerr << "Failed to open KVS: " << static_cast<int>(open_res.error()) << std::endl; |
| 79 | + return 1; |
| 80 | + } |
| 81 | + score::mw::per::kvs::Kvs kvs = std::move(open_res.value()); |
| 82 | + |
| 83 | + // Set a key-value pair |
| 84 | + kvs.set_value("username", score::mw::per::kvs::KvsValue("alice")); |
| 85 | + |
| 86 | + // Read a value (will fall back to default if not set) |
| 87 | + score::mw::per::kvs::Result<score::mw::per::kvs::KvsValue> get_res = kvs.get_value("username"); |
| 88 | + if (get_res) { |
| 89 | + std::cout << "username: " << get_res.value().as_string() << std::endl; |
| 90 | + } |
| 91 | + |
| 92 | + // Read a default value (not set, but present in defaults) |
| 93 | + auto get_default = kvs.get_value("language"); |
| 94 | + if (get_default) { |
| 95 | + std::cout << "language (default): " << get_default.value().as_string() << std::endl; |
| 96 | + } |
| 97 | + |
| 98 | + // Check if a key exists (only if written) |
| 99 | + if (kvs.key_exists("username").value_or(false)) { |
| 100 | + std::cout << "username exists!" << std::endl; |
| 101 | + } |
| 102 | + |
| 103 | + // Remove a key |
| 104 | + kvs.remove_key("username"); |
| 105 | + |
| 106 | + // List all keys (only written keys, not defaults) |
| 107 | + auto keys_res = kvs.get_all_keys(); |
| 108 | + if (keys_res) { |
| 109 | + std::cout << "All keys in KVS:" << std::endl; |
| 110 | + for (const auto& key : keys_res.value()) { |
| 111 | + std::cout << " " << key << std::endl; |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + // Flush changes to disk |
| 116 | + kvs.flush(); |
| 117 | + |
| 118 | + return 0; |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +## 3. Using the Rust Implementation |
| 123 | + |
| 124 | +### 3.1 Basic Usage |
| 125 | + |
| 126 | +From `examples/basic.rs`: |
| 127 | +```rust |
| 128 | +use rust_kvs::prelude::*; |
| 129 | +use std::collections::HashMap; |
| 130 | +use tempfile::tempdir; |
| 131 | + |
| 132 | +fn main() -> Result<(), ErrorCode> { |
| 133 | + let dir = tempdir()?; |
| 134 | + let dir_string = dir.path().to_string_lossy().to_string(); |
| 135 | + let instance_id = InstanceId(0); |
| 136 | + |
| 137 | + // Build KVS instance |
| 138 | + let builder = KvsBuilder::new(instance_id) |
| 139 | + .dir(dir_string.clone()) |
| 140 | + .kvs_load(KvsLoad::Optional); |
| 141 | + let kvs = builder.build()?; |
| 142 | + |
| 143 | + // Set values |
| 144 | + kvs.set_value("number", 123.0)?; |
| 145 | + kvs.set_value("bool", true)?; |
| 146 | + kvs.set_value("string", "First")?; |
| 147 | + |
| 148 | + // Get value |
| 149 | + let value = kvs.get_value("number")?; |
| 150 | + println!("number = {:?}", value); |
| 151 | + |
| 152 | + Ok(()) |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +### 3.2 Snapshots Example |
| 157 | + |
| 158 | +From `examples/snapshots.rs`: |
| 159 | +```rust |
| 160 | +let max_count = kvs.snapshot_max_count() as u32; |
| 161 | +for index in 0..max_count { |
| 162 | + kvs.set_value("counter", index)?; |
| 163 | + kvs.flush()?; |
| 164 | + println!("Snapshot count: {:?}", kvs.snapshot_count()); |
| 165 | +} |
| 166 | + |
| 167 | +// Restore a snapshot |
| 168 | +kvs.snapshot_restore(SnapshotId(2))?; |
| 169 | +``` |
| 170 | + |
| 171 | +### 3.3 Defaults Example |
| 172 | + |
| 173 | +From `examples/defaults.rs`: |
| 174 | +```rust |
| 175 | +// Create defaults file and build KVS instance with defaults |
| 176 | +create_defaults_file(dir.path().to_path_buf(), instance_id)?; |
| 177 | +let builder = KvsBuilder::new(instance_id) |
| 178 | + .dir(dir_string) |
| 179 | + .defaults(KvsDefaults::Required); |
| 180 | +let kvs = builder.build()?; |
| 181 | + |
| 182 | +// Get default value |
| 183 | +let k1_value = kvs.get_default_value("k1")?; |
| 184 | +println!("k1 = {:?}", k1_value); |
| 185 | +``` |
| 186 | +## 4. Default value file example |
| 187 | +This file should be placed in the working directory: |
| 188 | +```json |
| 189 | +{ |
| 190 | + "language": "en", |
| 191 | + "theme": "dark", |
| 192 | + "timeout": 30 |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +**Important:** |
| 197 | +- If you open the KVS with `.need_defaults_flag(true)`, the file must exist. |
| 198 | +- The KVS will use these defaults for any key not explicitly set. |
| 199 | +- You must also provide a CRC file (e.g., `defaults.json.crc`) alongside the defaults file. This CRC file is generated using the Adler-32 checksum algorithm, as implemented in the codebase. The CRC ensures the integrity of the defaults file at runtime. |
| 200 | +## 5. More Examples |
| 201 | +- See `src/cpp/tests/` for C++ test scenarios and usage patterns. |
| 202 | +- See `src/rust/rust_kvs/examples/` for Rust usage patterns. |
0 commit comments