53#include "librats/util/rats_export.h"
66#include <unordered_map>
71#include <condition_variable>
123 const std::vector<uint8_t>& d, uint64_t ts,
124 const std::string& peer_id)
125 : key(k), type(t), data(d), timestamp_ms(ts),
126 origin_peer_id(peer_id), checksum(0), deleted(false) {
127 calculate_checksum();
151 static bool deserialize(
const uint8_t* data,
size_t size,
size_t offset,
156 static bool deserialize(
const std::vector<uint8_t>& data,
size_t offset,
299 bool put(
const std::string& key,
const std::string& value);
300 bool put(
const std::string& key, int64_t value);
301 bool put(
const std::string& key,
double value);
302 bool put(
const std::string& key,
const std::vector<uint8_t>& value);
309 std::optional<std::string>
get_string(
const std::string& key)
const;
310 std::optional<int64_t>
get_int(
const std::string& key)
const;
311 std::optional<double>
get_double(
const std::string& key)
const;
312 std::optional<std::vector<uint8_t>>
get_binary(
const std::string& key)
const;
313 std::optional<librats::Json>
get_json(
const std::string& key)
const;
314 std::optional<StorageValueType>
get_type(
const std::string& key)
const;
321 bool has(
const std::string& key)
const;
322 std::vector<std::string>
keys()
const;
362 void on_peer_connected(
const PeerId& peer_id);
363 void on_peer_disconnected(
const PeerId& peer_id);
364 void on_peer_writable(
const PeerId& peer_id);
378 mutable std::mutex storage_mutex_;
379 std::map<std::string, StorageEntry> entries_;
385 mutable std::mutex sync_mutex_;
387 bool initial_sync_complete_;
388 std::chrono::steady_clock::time_point last_sync_time_;
393 bool streaming =
false;
394 bool started =
false;
397 std::chrono::steady_clock::time_point last_start{};
399 std::unordered_map<PeerId, PeerSync, PeerId::Hash> peers_;
405 uint64_t sync_epoch_ = 0;
409 std::atomic<bool> sync_running_{
false};
410 std::thread sync_thread_;
411 std::condition_variable sync_cv_;
414 size_t batch_bytes_{0};
415 std::chrono::milliseconds sync_interval_{0};
418 mutable std::mutex stats_mutex_;
419 StorageStatistics stats_;
426 std::atomic<bool> running_;
427 std::thread persistence_thread_;
428 std::condition_variable persistence_cv_;
429 std::mutex persistence_mutex_;
433 static constexpr uint8_t OP_ENTRY = 1;
434 static constexpr uint8_t OP_SYNC_REQUEST = 2;
435 static constexpr uint8_t OP_SYNC_CHUNK = 3;
438 static constexpr uint8_t FLAG_LAST = 0x01;
444 static constexpr size_t kMaxInboundMessage = 8 * 1024 * 1024;
449 void persistence_thread_loop();
450 void start_sync_thread();
451 void stop_sync_thread();
452 void sync_thread_loop();
454 enum class ChunkResult {
460 ChunkResult stream_snapshot_chunk(
const PeerId& peer);
463 void schedule_snapshot(
const PeerId& peer,
bool requested_by_peer);
466 bool put_internal(
const std::string& key, StorageValueType type,
467 const std::vector<uint8_t>& data,
468 uint64_t timestamp_ms = 0,
469 const std::string& origin_peer_id =
"",
470 bool broadcast =
true);
473 std::vector<uint8_t> serialize_value(int64_t value)
const;
474 std::vector<uint8_t> serialize_value(
double value)
const;
475 std::vector<uint8_t> serialize_value(
const std::string& value)
const;
476 int64_t deserialize_int64(
const std::vector<uint8_t>& data)
const;
477 double deserialize_double(
const std::vector<uint8_t>& data)
const;
478 std::string deserialize_string(
const std::vector<uint8_t>& data)
const;
483 void replicate_entry(
const StorageEntry& entry,
const PeerId* except);
484 void broadcast_entry(
const StorageEntry& entry) { replicate_entry(entry,
nullptr); }
485 void forward_entry(
const StorageEntry& entry,
const PeerId& except) {
486 replicate_entry(entry, &except);
488 void send_sync_request(
const PeerId& peer_id);
491 bool apply_remote_entry(
const StorageEntry& entry, StorageChangeEvent* out_event);
493 uint32_t apply_chunk(
const PeerId& from,
const uint8_t* data,
size_t size, uint32_t count);
496 std::string get_data_file_path()
const;
497 std::string get_index_file_path()
const;
500 bool write_data_file();
501 bool read_data_file();
505 static void sanitize_config(StorageConfig& config);
506 uint64_t get_current_timestamp_ms()
const;
507 std::string get_our_peer_id()
const;
508 void notify_change(
const StorageChangeEvent& event);
Lightweight byte container aliases and a non-owning byte view.
Non-owning view over a contiguous run of bytes.
StorageManager - Distributed key-value storage with peer synchronization.
librats::Json get_statistics_json() const
bool request_sync()
Request a full snapshot from one connected peer.
bool put(const std::string &key, double value)
bool put(const std::string &key, const std::vector< uint8_t > &value)
std::optional< std::string > get_string(const std::string &key) const
bool put_json(const std::string &key, const librats::Json &value)
StorageManager & operator=(const StorageManager &)=delete
std::optional< librats::Json > get_json(const std::string &key) const
void set_config(const StorageConfig &config)
Replace the configuration.
std::vector< std::string > keys() const
const StorageConfig & get_config() const
StorageManager(const StorageManager &)=delete
void set_change_callback(StorageChangeCallback callback)
StorageSyncStatus get_sync_status() const
void attach(NodeContext &ctx) override
std::optional< double > get_double(const std::string &key) const
std::optional< std::vector< uint8_t > > get_binary(const std::string &key) const
bool put(const std::string &key, int64_t value)
StorageManager(const StorageConfig &config=StorageConfig())
Constructor.
std::optional< StorageValueType > get_type(const std::string &key) const
bool remove(const std::string &key)
std::optional< int64_t > get_int(const std::string &key) const
StorageStatistics get_statistics() const
bool has(const std::string &key) const
void set_sync_complete_callback(StorageSyncCompleteCallback callback)
~StorageManager() override
Destructor - saves data and cleans up resources.
bool put(const std::string &key, const std::string &value)
std::vector< std::string > keys_with_prefix(const std::string &prefix) const
A pluggable network subsystem.
A small, self-contained JSON value type for librats.
StorageValueType string_to_storage_value_type(const std::string &str)
StorageValueType
Value types supported by the distributed storage.
uint32_t storage_calculate_crc32(const void *data, size_t length)
std::function< void(const StorageChangeEvent &)> StorageChangeCallback
Callback function types for storage events.
StorageSyncStatus
Storage synchronization status.
StorageOperation
Storage operation types for change events.
std::function< void(bool success, const std::string &error_message)> StorageSyncCompleteCallback
std::string storage_value_type_to_string(StorageValueType type)
A lightweight handle to a connected peer.
Self-certifying peer identity.
The narrow contract a subsystem needs from the node — and nothing more.
Storage change event - passed to change callbacks.
std::vector< uint8_t > old_data
std::vector< uint8_t > new_data
std::string origin_peer_id
StorageOperation operation
static constexpr uint32_t kMaxSyncBatchBytes
Ceiling on sync_batch_bytes, for the same reason.
std::string data_directory
static constexpr uint32_t kMaxValueSize
Ceiling on max_value_size: the default connection low-water mark.
uint32_t sync_min_interval_ms
Minimum gap between two snapshots served to the same peer.
std::string database_name
uint32_t compaction_threshold
uint32_t sync_batch_bytes
Target payload size of one snapshot chunk.
static constexpr uint32_t kMinSyncBatchBytes
Floor on sync_batch_bytes: below this a snapshot costs more in per-message framing and round trips th...
Storage entry - represents a single key-value pair in the database.
void serialize_into(std::vector< uint8_t > &out) const
Append the serialized entry to out.
std::vector< uint8_t > data
void calculate_checksum()
static bool deserialize(const uint8_t *data, size_t size, size_t offset, StorageEntry &entry, size_t &bytes_read)
Deserialize one entry from data[offset..], setting bytes_read to the bytes it consumed.
bool verify_checksum() const
StorageEntry(const std::string &k, StorageValueType t, const std::vector< uint8_t > &d, uint64_t ts, const std::string &peer_id)
std::vector< uint8_t > serialize() const
std::string origin_peer_id
bool wins_over(const StorageEntry &other) const
static bool deserialize(const std::vector< uint8_t > &data, size_t offset, StorageEntry &entry, size_t &bytes_read)
Vector overload of the above; the wire path uses the pointer form to parse straight out of the receiv...
size_t serialized_size() const
Serialized size in bytes, without serializing.
uint64_t sync_requests_sent
std::chrono::steady_clock::time_point last_sync_time
uint64_t sync_requests_received
uint64_t total_data_bytes
uint64_t disk_usage_bytes
uint64_t sync_chunks_sent
StorageSyncStatus sync_status
uint64_t sync_chunks_received
uint64_t resyncs_scheduled