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

Use effects to initialize new projections #255

Merged
merged 3 commits into from
Mar 27, 2024

Conversation

the-mikedavis
Copy link
Member

khepri_machine shouldn't initialize projections in its apply/3 callback. That function might be called by Ra when recovering and folding through the log from a snapshot. If the log contains a #register_projection{} command then Khepri will mistakenly create the ETS table and fill it with whatever is in the tree at that moment. Effects like #trigger_projection{} are discarded during recovery, so restarting a server could lead to inconsistencies in projection tables between cluster members.

Instead we can use an aux effect to perform the side effects like we do already for restore_projections and #trigger_projection{} effects. This will be correctly discarded when Khepri is recovering. Then projections will be correctly restored once the machine is recovered with the existing state_enter(recovered, State) callback implementation.

To make the #register_projection{} apply/3 clause pure I've added a helper khepri_machine:has_projection/2. Exposing it as khepri:has_projection/2 isn't strictly necessary for this fix but it could be a nice win to take advantage of in the server. Currently we submit #register_projection{} commands every time the broker starts and rely on idempotency. A large projection function could be a non-trivial amount of data to store in the log though, so it would be nice to skip the command if we can detect that the projection is already registered.

@the-mikedavis the-mikedavis added the bug Something isn't working label Mar 19, 2024
@the-mikedavis the-mikedavis self-assigned this Mar 19, 2024
@the-mikedavis the-mikedavis marked this pull request as draft March 19, 2024 20:48
@the-mikedavis the-mikedavis force-pushed the md-has-projection/2-and-effect-free-register branch from cb81c8b to b509150 Compare March 19, 2024 20:53
Copy link

codecov bot commented Mar 19, 2024

Codecov Report

Attention: Patch coverage is 97.22222% with 1 lines in your changes are missing coverage. Please review.

Project coverage is 89.75%. Comparing base (2151d8a) to head (06712a1).

Files Patch % Lines
src/khepri.erl 87.50% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #255      +/-   ##
==========================================
+ Coverage   89.34%   89.75%   +0.41%     
==========================================
  Files          20       20              
  Lines        2975     2998      +23     
==========================================
+ Hits         2658     2691      +33     
+ Misses        317      307      -10     
Flag Coverage Δ
erlang-24 88.75% <97.22%> (+0.52%) ⬆️
erlang-25 88.69% <97.22%> (+0.42%) ⬆️
erlang-26 89.45% <97.22%> (+0.55%) ⬆️
os-ubuntu-latest 89.75% <97.22%> (+0.44%) ⬆️
os-windows-latest 89.45% <97.22%> (+0.48%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

This function checks if a projection is currently registered. A caller
might want to use it instead of sending `#register_projection{}`
commands and relying on idempotency because `#register_projection{}`
commands contain a horus standalone function, and the horus function's
beam code can consume a non-trivial amount of data in the Ra log.

We will use the `khepri_machine:has_projection/2` helper function in
the child commit to check whether a projection exists within the
`apply/3` clause that handles `#register_projection{}` commands so that
we can check for existing projections without the side effect of
creating an ETS table.
@the-mikedavis the-mikedavis force-pushed the md-has-projection/2-and-effect-free-register branch from b509150 to c3ae51f Compare March 19, 2024 20:56
@the-mikedavis the-mikedavis marked this pull request as ready for review March 19, 2024 20:56
src/khepri_machine.erl Outdated Show resolved Hide resolved
src/khepri_machine.erl Outdated Show resolved Hide resolved
test/projections.erl Outdated Show resolved Hide resolved
Previously the machine would initialize projections in-place within
`apply/3`. That would cause projections to be restored ahead of time
when the machine was recovering if it applied a `#register_projection{}`
command.

We can use an aux effect instead for the creation and initial filling
of the projection's ETS table. Aux effects are skipped during recovery.
Once the machine is fully recovered and the server enters the
`recovered` state, we emit a `restore_projections` aux effect that
properly (re)creates and fills the projections. (This is already done by
`khepri_machine`.)
This increases coverage for the projection related functions in the
`khepri` API.
@the-mikedavis the-mikedavis force-pushed the md-has-projection/2-and-effect-free-register branch from c3ae51f to 06712a1 Compare March 26, 2024 22:19
Copy link
Member

@dumbbell dumbbell left a comment

Choose a reason for hiding this comment

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

Thank you!

@dumbbell dumbbell merged commit e3b7b7b into main Mar 27, 2024
12 checks passed
@dumbbell dumbbell deleted the md-has-projection/2-and-effect-free-register branch March 27, 2024 14:19
@dumbbell dumbbell added this to the v0.13.0 milestone Mar 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants