Skip to content
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

Added initial config store #249

Merged
merged 10 commits into from
Nov 13, 2023
Merged

Added initial config store #249

merged 10 commits into from
Nov 13, 2023

Conversation

jaytaph
Copy link
Member

@jaytaph jaytaph commented Nov 10, 2023

Initial attempt on setting up a general config store.

It can serve from multiple backends (jsonfile, memory, sqlite).

@jaytaph jaytaph requested review from emwalker, Kiyoshika, neuodev and a team November 10, 2023 12:18
src/config.rs Outdated
description: String,
}

trait StorageAdapter {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any adapter that implements this can be used as a storage engine for the configuration store.

/// Configuration store is the place where the gosub engine can find all configurable options
pub struct ConfigStore {
/// A hashmap of all settings so we can search o(1) time
settings: HashMap<String, Setting>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Settings are only the setting values.. they are not stored in SettingsInfo, which is only the template data for the configuration

/// A hashmap of all setting descriptions
settings_info: HashMap<String, SettingInfo>,
/// Keys of all settings so we can iterate keys easily
setting_keys: Vec<String>,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we need this, but if we need to iterate all keys, we need to store them in a vec as well.


// preload the settings if requested
if preload {
let all_settings = store.storage.get_all_settings();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to preload all settings from a storage engine, or lazy load them when needed. It depends a bit on the latency of the storage engine (ie: remote configuration would take a long time)

src/config.rs Outdated
}

/// Returns a list of keys that mathces the given search string (can use *)
fn find_setting_keys(&self, search: &str) -> Vec<String> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows you to use "dns.resolv*" and it will match every key that starts with this. Easy for filtering

src/config.rs Outdated

// Panic if we can't find the setting, and we don't have a default
if default.is_none() {
panic!("Setting {} not found", key);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should we do if we cannot find the config, and there is no default given?

Should we even use a default, as we have defaults in the settingsinfo per config setting anyway

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed this into returning the default value as defined in the setting info for that key. This means we do not pass a default value when getting the key.

// m:foo,bar,baz

/// Converts a string to a setting or None when the string is invalid
pub(crate) fn from_string(p0: &str) -> Option<Setting> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A setting can have different types. bools, ints, strings etc. This tries to accomodate that

@@ -0,0 +1,46 @@
{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This config file is imported into the engine lib binary, but it allows for a more flexible setup than incode configuration.

default.unwrap()
}

pub fn set(&mut self, key: &str, value: Setting) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not do a check to see if the setting type is actually allowed.

For instance, you can set a bool (b:true) on a uint typed setting.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has been fixed as well.. it will check the type you add against the type of the default setting.

@jaytaph jaytaph marked this pull request as ready for review November 11, 2023 12:50
Map(Vec<String>),
}

impl Serialize for Setting {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serialize / deserialize are needed for using serde_json in the json-adapter. It will convert a Setting directory to a string


impl StorageAdapter for JsonStorageAdapter {
fn get_setting(&self, key: &str) -> Option<Setting> {
match self.elements.get(key) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically the json adapter reads the whole json during init, and not during every get_setting call.. This would create a secondary layer of caching and doesn't reflect any changes that are done on disk. We might want to reread the whole json on each json file, OR do something smarter like checking access/modify time


let json = serde_json::to_string_pretty(&self.elements).expect("failed to serialize");

file.set_len(0).expect("failed to truncate file");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we truncate the current file, since this is the file that is guarded by the mutex


let mut adapter = JsonStorageAdapter {
path: path.to_string(),
file_mutex: Arc::new(Mutex::new(file)),
Copy link
Member Author

@jaytaph jaytaph Nov 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A mutex is used so we can easily use this is a multi-threaded environment. But we should lock at get/set_setting, not in the read/write functions.

Copy link
Collaborator

@emwalker emwalker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with suggestions.

src/config.rs Outdated Show resolved Hide resolved
src/config.rs Outdated

impl ConfigStore {
/// Creates a new store with the given storage adapter and preloads the store if needed
pub fn new(storage: Box<dyn StorageAdapter>, preload: bool) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we can avoid using Box<dyn ...> if we replace it with a generic struct parameter. But in this specific case I doubt it would make a difference, since this is not (presumably) critical path code.

}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add this line to the Makefile for this command:

diff --git a/Makefile b/Makefile
index e25ba44..7a362d3 100644
--- a/Makefile
+++ b/Makefile
@@ -36,6 +36,7 @@ test_fmt:
 test_commands:
        cargo run --bin html5-parser-test >/dev/null
        cargo run --bin parser-test >/dev/null
+       cargo run --bin config-store list >/dev/null
 
 help: ## Display available commands
        echo "Available make commands:"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add settings.db to .gitignore or put it under a tmp/ directory that is ignored:

diff --git a/.gitignore b/.gitignore
index 5ce6f87..0a3c97f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ target/
 *.swp*
 
 # Local testing 
-local/
\ No newline at end of file
+local/
+settings.db

2023-11-11_11-05

src/bin/config-store.rs Outdated Show resolved Hide resolved
src/bin/config-store.rs Outdated Show resolved Hide resolved
src/bin/config-store.rs Outdated Show resolved Hide resolved
src/config.rs Outdated Show resolved Hide resolved
src/config.rs Outdated Show resolved Hide resolved
src/config/settings.rs Outdated Show resolved Hide resolved
src/config/settings.rs Show resolved Hide resolved
@jaytaph jaytaph merged commit 9930d4b into main Nov 13, 2023
4 checks passed
@jaytaph jaytaph deleted the config branch November 13, 2023 08:55
@@ -101,11 +101,11 @@ fn main() {
}
}
Commands::Set { key, value } => {
store.set(&key, Setting::from_str(&value).expect("incorrect value"));
store.set(&key, Setting::from_string(&value).expect("incorrect value"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious why this was reverted. The name from_str is common in Rust, while the name from_string looks like someone didn't know about from_str.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emwalker this is due to an warning that is emitted by rust itself... it clashes with a standard from_str that is found in a ::std trait and could cause conflicts

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, gotcha. Perhaps that means we should be implementing that trait instead of defining from_string. It would not be hard.

impl FromStr for Setting {
  fn from_str(value: &str) -> Self {
    todo!()
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants