-
Notifications
You must be signed in to change notification settings - Fork 552
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
watcher refactor #3471
watcher refactor #3471
Conversation
b025e4e
to
fd0ee99
Compare
`try_new()` here is used as constructor, which is what `new` is for with less boilerplate.
They don't actually need it.
That way, all objects go away and it will be nothing more than a task around a channel.
As `Watcher` really adds nothing.
070e3e1
to
cd8e450
Compare
50565d7
to
1cb6451
Compare
The idea is that we don't parallelize over a channel anymore, but instead just process filesystem events, one at a time. This would allow each handler to become a function that gets its state passed, and makes all the necessary calls verbatim, which in turn makes it easy to follow what's happening. If anything becomes to slow due to the serialization of event processing, selective parallelization can be re-added.
That way, we get `tauri::Event`, without the somewhat confusing module name `events`.
Previously, each file change both in `.git` as well as in the worktree would cause a complete recomputation. This computation included opening a git repository at least once (probaby more often), to make an 'is-ignored' check. The latter is very expensive in `git2` and gets more expensive the more files there are. Now the repository is opened when needed, and we re-use it for all applicable file paths.
…tation for correctness
Previously it would watch every registered project, which could incur more work on all parts of the application than necessary. Now UI sends an event that indicates which project is active, allowing the watch to be setup in that very moment. It's worth noting that the previously watched project is automatically deregistered.
@@ -29,6 +29,7 @@ backoff = "0.4.0" | |||
backtrace = { version = "0.3.71", optional = true } | |||
chrono = { version = "0.4.37", features = ["serde"] } | |||
console-subscriber = "0.2.0" | |||
crossbeam-channel = "0.5.12" |
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.
Is there a reason for crossbeam channels vs using tokio's channels?
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.
Actually, tokio
channels might be usable here and I didn't try. It would certainly be preferable if it worked.
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.
Let's try tokio then, lmk if it doesn't work for some reason. Just trying to reduce our number of dependencies.
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 did take a look and realised that tokio::sync::broadcast
is indeed able to provide the multi-multi case. Probably last time I didn't really look there though since it requires async
, whereas this code is all non-async.
crossbeam-channel
was previously used in the dependency graph by 5 other dependencies and isn't adding to the compile time.
Do you still think crossbeam-channel
should be replaced with tokio::sync::broadcast
? If so, I'd probably go with block_on
where needed as this implementation relies on std::thread::scope()
. Turning it into async
-proper for parallelisation would be more effort.
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's probably fine, we can move more toward async over time.
Also TIL about std::thread::scope()
- neat!
crates/gitbutler-tauri/tests/watcher/handler/push_project_to_gitbutler.rs
Outdated
Show resolved
Hide resolved
Thanks for starting the review, @Qix- ! I recommend to PS: @mtsgrd I replied on top of your replies in some comments as what I saw was out of date, not because I thought I had to add to them. |
The `pure` functions were from a time where a `Handler` couldn't be instantiated in full for tests, but that is not the case anymore, so there isn't any use for the added complexity.
- turn `static` into `const`
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.
Thanks for the review! I think I have addressed all comments thus far, please let me know if anything else comes up.
Thanks! |
This PR attemps to minimize the watcher implementation to only the needed complexity.
Based on the analysis in #3447.
Tasks
try_from()
implementation becomes a constructor method.run()
is accidental (it was)Handler
needs to be managed - are they used and if so, how? Those who aren't shouldn't be managed.Clone
fromWatcher
by allowing communication to it using a channelClone
, but the watcher doesn't have to be.This is a precondition for removing all the interior Send+Sync and locking protections,
while the Watcher will have to watch in the background and thus be in a loop.
Handler
as function or method.pure
suffixed function and fixup tests to allow dissolve the last remaining private eventsevents::Event
justEvent
.gitignore
at app runtime) of ignore-filter to see if it can be removed/improved to be fastergit clone https://github.com/BurntSushi/ripgrep
, thengco @~200 -- crates/
. After each run, the worktree was reset to@
.master
Notes for the Reviewer
fetch
andpush
GB repo functionality, maybe much more. Better test everything that is triggered by watchers.git checkout @~100 -- src/
or similar as it will still emit a lot of debug messages that are buried deeply in thecore
part. This makes it seem like a lot is happening, even though that's not the case anymore in comparison to the before-times.core
to allow launching the application in DEBUG mode without being spammed with low-level information. Instead, DEBUG is now used for select information, and everything else was demoted to theTRACE
level. It's probably the way things go, what's useful inDEBUG
one day is better asTRACE
in another as the focus changes.Application Bugs 📋
These bugs were encountered during testing.
file_monitor.rs
,.git
has to be a directory which isn't the case for worktrees.Out of Scope but TODO
git::credential::Helpers::from_path()
is used in tests, but depends on the actualHOME
environment variables, isn't isolatedfrom_path()
implementations as well.My Notes
Definitely not a mandatory read to review this PR.
is_path_ignored()
in many places, given that the dispatcher filter already avoids ignored files (and can do so much more efficiently, but we must assure that it always gets most recent information .gitignore files as the watcher is long-running).Handlers
should have no need. One can hope they are isolated which they are probably not as they are all managed in tauri. This should probably be rolled back as much as possible.Send+Sync
+ shared state andClone
are poison, and a way to feel unsafe even without the keyword. In such an environment, one has no garantuees anymore, so one will want all code not to be dealing with that.Send+Sync
incore
as well, even though it might also be a good hint at how thesecore
instances can be used. My feeling is that many will need a tauri-specific wrapper, so thatcore
can be pure.Send+Sync
, many handlers are just a couple of lines. One handler is just sending an event, effectively.event queue
system is actually very cleverly multi-threading everything. Each event coming in is processed in its own (or pooled) thread, and as each even can spawn new events, these will equally be queued and run in parallel. This is why everything isSend + Sync
in the watcher..git/GB_Flush
file, I wonder if this file-based notification system is truly necessary, or if that could be kept intauri
. Maybe the plan is to support multiple windows to be open on the same repository (rather than prevent it), which probably would make this file-based approach necessary, and maybe even the most simple solution if filesystem events can be assumed to be reliable.core
at some point,.context()
calls and it feels these are 'just there to be there' even though the callee probably could deliver decent error messages from the beginning (and maybe already does so).