-
Notifications
You must be signed in to change notification settings - Fork 269
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
Refactor and clean up the SyncService #4543
base: main
Are you sure you want to change the base?
Conversation
Previously we had a lock protecting an empty value, but the logic wants to protect a bunch of data in the SyncService. Let's do the usual thing and create a SyncServiceInner which holds the data and protect that with a lock.
From cambridge a scheduler is defined as: > someone whose job is to create or work with schedules While supervisor is defined as: > a person whose job is to supervise someone or something Well ok, that doesn't tell us much, supervise is defined as: > to watch a person or activity to make certain that everything is done correctly, safely, etc.: In conclusion, supervising a task is the more common and better understood terminology here I would say.
The supervisor is defined as two optional fields that are set and removed at the same time. This patch converts the two optional fields into a single optional struct. The fields inside the struct now aren't anymore optional. This ensures that they are always set and destroyed at the same time.
Now that the various match branches in the start and stop method of the SyncService are minimized we can remove the early returns. This should allow us to more easily add new branches.
0bc9547
to
5f62220
Compare
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.
Haven't looked at all of it, but some of the cosmetic changes seem plain wrong, as they break symmetry with most of the current code style in the UI crate.
@@ -67,48 +67,40 @@ pub enum State { | |||
Error, | |||
} | |||
|
|||
pub struct SyncService { | |||
/// Room list service used to synchronize the rooms state. |
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.
Why did you remove all the fields comments? :/
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.
Oh, one of them was lost in the move. Or do you mean all those should be replicated in the Inner
struct?
Anyways, I added the missing one back: 9ae0235.
match inner.state.get() { | ||
State::Idle | State::Terminated | State::Error => { | ||
// No need to stop if we were not running. | ||
return Ok(()); | ||
Ok(()) | ||
} | ||
State::Running => {} | ||
}; | ||
|
||
trace!("pausing sync service"); | ||
State::Running => { | ||
trace!("pausing sync service"); | ||
|
||
// First, request to stop the two underlying syncs; we'll look at the results | ||
// later, so that we're in a clean state independently of the request to | ||
// stop. | ||
// First, request to stop the two underlying syncs; we'll look at the results | ||
// later, so that we're in a clean state independently of the request to stop. | ||
|
||
// Remove the supervisor from our inner state and request the tasks to be | ||
// shutdown. | ||
let supervisor = inner.supervisor.take().ok_or_else(|| { | ||
error!("The supervisor was not properly started up"); | ||
Error::InternalSupervisorError | ||
})?; | ||
// Remove the supervisor from our inner state and request the tasks to be | ||
// shutdown. | ||
let supervisor = inner.supervisor.take().ok_or_else(|| { | ||
error!("The supervisor was not properly started up"); | ||
Error::InternalSupervisorError | ||
})?; | ||
|
||
supervisor.shutdown().await | ||
supervisor.shutdown().await | ||
} | ||
} |
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.
This change feels like a regression: it adds one extra level of indent, for absolutely no extra value. I see the point of doing it in the other function because the body's size is so small there (in particular, there's no control flow), but it's not the case here, so this just adds visual clutter :/
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.
The body size of the SyncService::start()
and SyncService::stop()
methods was reduced to just a couple of lines in a previous commit, allowing us to do exactly this without introducing visual clutter from indentation.
The value becomes clear when you consider what needs to happen if someone adds another enum variant—something I plan to do.
Consider the examples:
First, the pattern where we flatten out one branch to remove the indentation:
enum Foo {
A,
B,
}
fn bar(foo: Foo) {
match foo {
// Do nothing if Foo::A.
A => return,
B => (),
}
// Do what we would have done in the B branch.
call_something_for_b();
}
Now the way it is in the pull-request:
enum Foo {
A,
B,
}
fn bar(foo: Foo) {
match foo {
// Do nothing if Foo::A.
A => (),
// Do what we need to do for B.
B => {
call_something_for_b();
},
}
}
When we add a new variant, I’ll either need to convert the first variant into the second, or introduce even more early returns, which would make the flow even less clear:
enum Foo {
A,
B,
C,
}
fn bar(foo: Foo) {
match foo {
// Do nothing if Foo::A.
A => return,
B => (),
C => {
// Do what we need for C, can't be flattened out like we did for B.
do_something_for_c();
return;
},
}
// Do what we would have done in the B branch.
call_something_for_b();
}
I hope we can agree that adding more variants would make the flow of execution even more convoluted.
In contrast, the second variant naturally provides a clear place to add a new branch, that's precisely what pattern matching is here for.
enum Foo {
A,
B,
C,
}
fn bar(foo: Foo) {
match foo {
A => (),
B => call_something_for_b(),
C => do_something_for_c(),
}
}
I would urge you to reconsider your stance on this pattern and code style, and to evaluate how flattening the branches of a pattern match might affect the extensibility of the codebase.
Early returns have their, in my opinion, limited use—but this ain't it, babe. 🎶
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.
That you planned to add another variant is something I wasn't aware of, and couldn't guess, so that makes sense in this case 👍
I would urge you to reconsider your stance on this pattern and code style, and to evaluate how flattening the branches of a pattern match might affect the extensibility of the codebase.
No, I'd rather have you reconsider your stance: more early returns make for fewer indent levels, and more "linear" / "simple" / "beautiful" code, where all the assumptions are checked upfront, and then the actual work is done later. Agree to disagree here :-)
let report = | ||
TerminationReport { is_error, has_expired, origin: TerminationOrigin::EncryptionSync }; |
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.
I don't see the point of the report
variables: they only add one level of "indirection" (some extra thing to keep in mind for a reader), they create one extra binding for the compiler frontend to handle, and they're used only once, so might as well be kept inlined at use.
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.
I prefer the formatting of the two statements compared to the formatting of the single statement, but ok: b38e8b9.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4543 +/- ##
==========================================
+ Coverage 85.40% 85.42% +0.01%
==========================================
Files 285 285
Lines 32213 32213
==========================================
+ Hits 27511 27517 +6
+ Misses 4702 4696 -6 ☔ View full report in Codecov by Sentry. |
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.
Looks great, thank you for doing this.
These are mostly internal changes and don't contain any behavioral change, except for some edge cases that are now a bit better handled.
A review commit by commit should ease the task a bit, but reading the whole file shouldn't be too terrible either, it's not that big.