-
Notifications
You must be signed in to change notification settings - Fork 20
impl: backend design change #150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| @startuml Persistency class diagram | ||
|
|
||
| package "kvs_builder" { | ||
| ~class KvsInner { | ||
| ~parameters: KvsParameters | ||
| ~data: KvsData | ||
| } | ||
|
|
||
| +class KvsBuilder { | ||
| {static} -KVS_POOL: list<KvsInner> | ||
| -parameters: KvsParameters | ||
|
|
||
| +new(instance_id: InstanceId): KvsBuilder | ||
| {static} +max_instances(): usize | ||
| +defaults(mode: KvsDefaults): KvsBuilder | ||
| +kvs_load(mode: KvsLoad): KvsBuilder | ||
| +backend_parameters(parameters: KvsMap): KvsBuilder | ||
| +build(): Kvs | ||
| } | ||
|
|
||
| KvsInner <-- KvsBuilder | ||
| } | ||
|
|
||
| package "kvs_api" { | ||
| +interface KvsApi { | ||
| +reset() | ||
| +reset_key(key: string) | ||
| +get_all_keys(): list<string> | ||
| +key_exists(key: string): bool | ||
| +get_value(key: string): KvsValue | ||
| +get_value_as<T>(key: string): T | ||
| +get_default_value(key: string): KvsValue | ||
| +is_value_default(key: string): bool | ||
| +set_value<T>(key: string, value: T) | ||
| +remove_key(key: string) | ||
| +flush() | ||
| +snapshot_count(): usize | ||
| +snapshot_max_count(): usize | ||
| +snapshot_restore(snapshot_id: SnapshotId) | ||
| } | ||
| } | ||
|
|
||
| package "kvs" { | ||
| +class Kvs { | ||
| -data: KvsData | ||
| -parameters: KvsParameters | ||
| -backend: KvsBackend | ||
|
|
||
| ~new(data: KvsData, parameters: KvsParameters, backend: KvsBackend): Kvs | ||
| +parameters(): KvsParameters | ||
|
|
||
| +reset() | ||
| +reset_key(key: string) | ||
| +get_all_keys(): list<string> | ||
| +key_exists(key: string): bool | ||
| +get_value(key: string): KvsValue | ||
| +get_value_as<T>(key: string): T | ||
| +get_default_value(key: string): KvsValue | ||
| +is_value_default(key: string): bool | ||
| +set_value<T>(key: string, value: T) | ||
| +remove_key(key: string) | ||
| +flush() | ||
| +snapshot_count(): usize | ||
| +snapshot_max_count(): usize | ||
| +snapshot_restore(snapshot_id: SnapshotId) | ||
| } | ||
| } | ||
|
|
||
| KvsApi <|.. Kvs | ||
| KvsBuilder -- Kvs | ||
|
|
||
| package "kvs_backend_registry" { | ||
| +class KvsBackendRegistry { | ||
| {static} -REGISTERED_BACKENDS: map<string, KvsBackendFactory> | ||
|
|
||
| {static} ~from_name(name: string) -> Result<&Box<dyn KvsBackendFactory>, ErrorCode> | ||
| {static} ~from_parameters(parameters: KvsMap) -> Result<&Box<dyn KvsBackendFactory>, ErrorCode> | ||
|
|
||
| {static} +register(backend_factory: KvsBackendFactory) | ||
| } | ||
| } | ||
|
|
||
| KvsBackendRegistry .. KvsBuilder | ||
|
|
||
| package "kvs_backend" { | ||
| +interface KvsBackend { | ||
| +load_kvs(instance_id: InstanceId, snapshot_id: SnapshotId): KvsMap | ||
| +load_defaults(instance_id: InstanceId): KvsMap | ||
| +flush(instance_id: InstanceId, kvs_map: KvsMap) | ||
| +snapshot_count(instance_id: InstanceId): usize | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I didn't get the whole idea behind the snapshots, my understanding and some questions:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Those are valid points, but should be taken into consideration once requirements are modified. |
||
| +snapshot_max_count(): usize | ||
| +snapshot_restore(instance_id: InstanceId, snapshot_id: SnapshotId): KvsMap | ||
| } | ||
|
|
||
| +interface KvsBackendFactory { | ||
| +create(parameters: KvsMap): KvsBackend | ||
| } | ||
| } | ||
|
|
||
| package "json_backend" { | ||
| ~class JsonBackend { | ||
| +load_kvs(instance_id: InstanceId, snapshot_id: SnapshotId): KvsMap | ||
| +load_defaults(instance_id: InstanceId): KvsMap | ||
| +flush(instance_id: InstanceId, kvs_map: KvsMap) | ||
| +snapshot_count(instance_id: InstanceId): usize | ||
| +snapshot_max_count(): usize | ||
| +snapshot_restore(instance_id: InstanceId, snapshot_id: SnapshotId): KvsMap | ||
| } | ||
|
|
||
| ~class JsonBackendFactory { | ||
| +create(parameters: KvsMap): KvsBackend | ||
| } | ||
| } | ||
|
|
||
| KvsBackend <-- Kvs | ||
|
|
||
| KvsBackendRegistry --> KvsBackendFactory | ||
| KvsBackendFactory -- KvsBackend | ||
|
|
||
| KvsBackend <|.. JsonBackend | ||
| KvsBackendFactory <|.. JsonBackendFactory | ||
| JsonBackendFactory -- JsonBackend | ||
|
|
||
| @enduml | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| //! Example for custom backend registration. | ||
| //! - Implementation of `KvsBackend` traits. | ||
| //! - Registration of custom backend. | ||
| //! - Creation of KVS instance utilizing custom backend. | ||
|
|
||
| use rust_kvs::prelude::*; | ||
| use tempfile::tempdir; | ||
|
|
||
| /// Mock backend implementation. | ||
| /// Only `load_kvs` is implemented. | ||
| struct MockBackend; | ||
|
|
||
| impl KvsBackend for MockBackend { | ||
| fn load_kvs( | ||
| &self, | ||
| _instance_id: InstanceId, | ||
| _snapshot_id: SnapshotId, | ||
| ) -> Result<KvsMap, ErrorCode> { | ||
| println!("`load_kvs` used"); | ||
| Ok(KvsMap::new()) | ||
| } | ||
|
|
||
| fn load_defaults(&self, _instance_id: InstanceId) -> Result<KvsMap, ErrorCode> { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn flush(&self, _instance_id: InstanceId, _kvs_map: &KvsMap) -> Result<(), ErrorCode> { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn snapshot_count(&self, _instance_id: InstanceId) -> usize { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn snapshot_max_count(&self) -> usize { | ||
| unimplemented!() | ||
| } | ||
|
|
||
| fn snapshot_restore( | ||
| &self, | ||
| _instance_id: InstanceId, | ||
| _snapshot_id: SnapshotId, | ||
| ) -> Result<KvsMap, ErrorCode> { | ||
| unimplemented!() | ||
| } | ||
| } | ||
|
|
||
| /// Mock backend factory implementation. | ||
| struct MockBackendFactory; | ||
|
|
||
| impl KvsBackendFactory for MockBackendFactory { | ||
| fn create(&self, _parameters: &KvsMap) -> Result<Box<dyn KvsBackend>, ErrorCode> { | ||
| Ok(Box::new(MockBackend)) | ||
| } | ||
| } | ||
|
|
||
| fn main() -> Result<(), ErrorCode> { | ||
| // Temporary directory. | ||
| let dir = tempdir()?; | ||
| let dir_string = dir.path().to_string_lossy().to_string(); | ||
|
|
||
| // Register `MockBackendFactory`. | ||
| KvsBackendRegistry::register("mock", || Box::new(MockBackendFactory))?; | ||
|
|
||
| // Build KVS instance with mock backend. | ||
| { | ||
| let instance_id = InstanceId(0); | ||
| let parameters = KvsMap::from([("name".to_string(), KvsValue::String("mock".to_string()))]); | ||
| let builder = KvsBuilder::new(instance_id) | ||
| .backend_parameters(parameters) | ||
| .defaults(KvsDefaults::Ignored); | ||
| let kvs = builder.build()?; | ||
|
|
||
| println!( | ||
| "KVS instance with mock backend - parameters: {:?}", | ||
| kvs.parameters() | ||
| ); | ||
| } | ||
|
|
||
| // Build KVS instance with JSON backend - default parameters. | ||
| { | ||
| let instance_id = InstanceId(1); | ||
| let builder = KvsBuilder::new(instance_id).defaults(KvsDefaults::Ignored); | ||
| let kvs = builder.build()?; | ||
|
|
||
| println!( | ||
| "KVS instance with default JSON backend - parameters: {:?}", | ||
| kvs.parameters() | ||
| ); | ||
| } | ||
|
|
||
| // Build KVS instance with JSON backend - `working_dir` set. | ||
| { | ||
| let instance_id = InstanceId(2); | ||
| let parameters = KvsMap::from([ | ||
| ("name".to_string(), KvsValue::String("json".to_string())), | ||
| ("working_dir".to_string(), KvsValue::String(dir_string)), | ||
| ]); | ||
| let builder = KvsBuilder::new(instance_id) | ||
| .backend_parameters(parameters) | ||
| .defaults(KvsDefaults::Ignored); | ||
| let kvs = builder.build()?; | ||
|
|
||
| println!( | ||
| "KVS instance with JSON backend - parameters: {:?}", | ||
| kvs.parameters() | ||
| ); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the proposed API, the user always have to wait until whole KVS is populated only then values are accessible.
Why not to allow direct access to the value and satisfy requirements for earliest possible availability?
With restriction that only read access is possible eg.
load_value(key: string): KvsValueThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, currently it is assumed that storage is fully memory-mapped. You can add it as a topic for requirements discussion.