-
Notifications
You must be signed in to change notification settings - Fork 44
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
Setting Overhaul #105
Setting Overhaul #105
Conversation
da2ce7
commented
Oct 24, 2022
•
edited
Loading
edited
- Move internal vocabulary from 'Configuration' with 'Settings'.
- Make use of a 'config' folder. Will generate new config files when needed.
- New Config is stored in json. And parsed without the need of the config crate.
- Automatically finds and moves old configuration to new format, (keeping a backup).
7dc2402
to
698f181
Compare
src/logging.rs
Outdated
|
||
if level == log::LevelFilter::Off { | ||
return; | ||
} | ||
|
||
INIT.call_once(|| { | ||
stdout_config(level); | ||
stdout_settings(level); |
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.
hi @da2ce7, in this case, "config" is a verb. The function stdout_config
sets up the logging. I was tempted to rename it to redirect_logging_to_stdout
or something like that.
hi @da2ce7, it looks good to me. In the future, something that could be useful is to allow overwriting default settings from env vars instead of from a I've seen that the crate we are using allows it. I think the https://github.com/josecelano/torrust-tracker/blob/issue-11-docker-support/bin/docker/run.sh#L6 And with K8S, you also have ConfigMaps. These are the ways that the container can get the configuration:
There is also another thing to consider. Some settings are secrets. From the application point of view, I suppose it does not matter. If we are using docker, we can decide that the whole configuration is passed as a mounted read-only file or as env vars. I do not know if one could be considered safer than the other. |
ACK 698f181 |
698f181
to
8e5caac
Compare
Needs Rebase. |
0fd33f2
to
3ab4828
Compare
src/databases/sqlite.rs
Outdated
impl SqliteDatabase { | ||
pub fn new(db_path: &str) -> Result<SqliteDatabase, r2d2::Error> { | ||
let cm = SqliteConnectionManager::file(db_path); | ||
pub fn new(settings: &Sqlite3DatabaseSettings) -> Result<SqliteDatabase, r2d2::Error> { | ||
let cm = SqliteConnectionManager::file(settings.database_file_path.as_path()); | ||
let pool = Pool::new(cm).expect("Failed to create r2d2 SQLite connection pool."); | ||
Ok(SqliteDatabase { pool }) | ||
} |
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.
hi @da2ce7, I always have doubts about this kind of change. I have a "rule" which is: "do not pass to a function more than what it needs". In this case, the constructor only needs the path for the DB file. In the first version, SqliteDatabase did not know anything about the Settings
, and now they are coupled. If you change the settings you also have to change this function.
Normally you would do that because you think in the future, the SqliteDatabase
might need other values.
Anyway, I think it's a nice generalization since we have more than one database driver. I would expect all drivers to have a similar constructor regardless of their specific parameters.
On the other hand, would it make sense to add a wrapper for the DB path like this:
pub struct Sqlite3DatabaseSettings {
pub database_file_path: Sqlite3DatabaseFilePath,
}
pub struct Sqlite3DatabaseFilePath {
pub file_path: PathBuf,
}
In general, I have another rule: "do not add a wrapper to a primitive type (or other types) if you do not have any rule (invariant) to enforce".
In this case, we do not have it unless we want to check that the file exists and contains SQLite data, which is, I think, a bad idea to do in the constructor. But maybe we can check that the path is for a file and not a directory.
But sometimes, I think these custom types are a good way to easily find where you are using them.
Anyway, I would probably do this:
pub fn new(db_file_path: &PathBuf) -> Result<SqliteDatabase, r2d2::Error> {
let cm = SqliteConnectionManager::file(db_file_path.as_path());
let pool = Pool::new(cm).expect("Failed to create r2d2 SQLite connection pool.");
Ok(SqliteDatabase { pool })
}
By the way, maybe we should use Path
instead of PathBuf
here. But I do not know very weel the difference. I think here we could use a Path
because we do not need to change it.
} | ||
|
||
#[derive(Error, Clone, Debug, Eq, Hash, PartialEq)] | ||
pub enum CommonSettingsError { |
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.
hi @da2ce7, what about GenericSettingsErrors
?
src/jobs/torrent_cleanup.rs
Outdated
|
||
pub fn start_job(config: &Configuration, tracker: Arc<TorrentTracker>) -> JoinHandle<()> { | ||
pub fn start_job(interval: u64, tracker: Arc<TorrentTracker>) -> JoinHandle<()> { |
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.
hi @da2ce7, this is an example where you decided to pass only what the function needs. I would like to know why you did it here.
src/jobs/tracker_api.rs
Outdated
.parse::<std::net::SocketAddr>() | ||
.expect("Tracker API bind_address invalid."); | ||
info!("Starting Torrust API server on: {}", bind_addr); | ||
pub fn start_job(settings: &ApiServiceSettings, tracker: Arc<TorrentTracker>) -> JoinHandle<()> { |
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.
hi @da2ce7, Another example where you reduce the data you pass to the function.
src/settings/manager.rs
Outdated
} | ||
|
||
#[test] | ||
fn it_should_write_and_read_and_write_default_config() { |
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.
@da2ce7 Do you mean "it should migrate config file from toml to json"?
And there are no assertions.
} | ||
|
||
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Hash)] | ||
pub struct CommonSettings { |
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.
@da2ce7 what's the difference between "global" and "common"?
|
||
#[derive(Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Debug, Copy, Clone, Hash, Display)] | ||
#[serde(rename_all = "snake_case")] | ||
pub enum LogFilterLevel { |
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.
@da2ce7 move to logging?
src/tracker/mod.rs
Outdated
CommonSettings, CommonSettingsBuilder, DatabaseSettingsBuilder, GlobalSettings, GlobalSettingsBuilder, LogFilterLevel, | ||
}; | ||
|
||
pub struct TrackerArgs { |
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.
@da2ce7 I think this struct is useful now, but in the long term, I think we should extract some responsibilities from the Tracker, for example persistence. I think we could have:
- One handler or controller fer each request type (announce, scrape, ...)
- An app service (announce is the same from HTTP and udp). It could be also a command or query with its handler. For example: AnnounceCommand with AnnounceCommandHandler.
- A TorrentService to add/update/remove torrents. We can trigger events at this level.
- The TorrentService can use a TorrentRepository (DB: SQLite or MySQL) and a TorrentCache (the current in-memory structure).
But it's just a vague idea I have. I wanted to draft something, but I have not had time. And I also wanted to get to know the repo better before spending time proposing this kind of change.
tests/udp.rs
Outdated
@@ -149,8 +171,8 @@ mod udp_tracker_server { | |||
} | |||
|
|||
/// Creates a new UdpTrackerClient connected to a Udp Tracker server | |||
async fn new_connected_udp_tracker_client(remote_address: &str) -> UdpTrackerClient { | |||
let udp_client = new_connected_udp_client(remote_address).await; | |||
async fn new_connected_udp_tracker_client(remote_socket: &SocketAddr) -> UdpTrackerClient { |
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.
@da2ce7 I like this change. We should try to normalize this variable name. We are using:
- remote_address
- remote_socket
- remote_ip
- remote_client
- client?
I think client_socket
is better because it's more specific. But I have not changed to avoid introducing more versions.
And I've just realized in this case, it's the server_socket
which makes my assumptions more valid that we should be more specific.
hi @da2ce7, it looks good to me! I've just added some minor comments. I agree on all refactors, but I'm not sure why you want to change the config file from |
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.
It looks to me, although I still do not know the reason for using JSON instead of TOML. is it only because you want to remove the dependency?
Considering to Rewrite this entire thing all-again. This time making full use of https://github.com/SergioBenitez/Figment |
879cee7
to
8fb6d05
Compare
Partial Rebase. Completed Database Settings. |
Closing, needs to be re-imagined, suggest using https://github.com/SergioBenitez/Figment as config framework library. |