-
Notifications
You must be signed in to change notification settings - Fork 2
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
Restructure alerts engine to merge new results into cache instead of delete/write #864
Merged
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
10a4b65
Base replace functionality, no cleanup or testing
robbie-sundstrom ff0b36b
Move into separate ETS engine
robbie-sundstrom 58d00f0
Add test for the ETS engine
robbie-sundstrom 759e0e8
Add alias for Engine.ETS
robbie-sundstrom f25f902
Merge branch 'main' into rs/refactor-rts-alerts-engine
robbie-sundstrom 09bc827
Move ETS methods to a new utilities module
robbie-sundstrom beea70c
PR comments to merge use :none as default value for alerts in ETS
robbie-sundstrom 59b7d81
remove unecessary comment
robbie-sundstrom File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
defmodule Engine.ETS do | ||
# Safely replaces table contents. | ||
# | ||
# ETS doesn't support atomic bulk writes, so we can't just clear the whole table | ||
# (:ets.delete_all_objects/1) and then insert all of the new entries (:ets.insert/2), | ||
# because that would leave the table completely empty for a short period, | ||
# causing any concurrent reads during that time to fail. | ||
# | ||
# Instead, we remove only the table entries that are absent from new_entries. | ||
def replace_contents(table, new_entry) when is_tuple(new_entry) do | ||
replace_contents(table, [new_entry]) | ||
end | ||
|
||
def replace_contents(table, new_entries) do | ||
new_keys = MapSet.new(new_entries, &elem(&1, 0)) | ||
current_table_keys = keys(table) | ||
|
||
removed_keys = MapSet.difference(current_table_keys, new_keys) | ||
Enum.each(removed_keys, &:ets.delete(table, &1)) | ||
|
||
:ets.insert(table, Enum.into(new_entries, [])) | ||
end | ||
|
||
# Returns a MapSet of all keys in the table. | ||
robbie-sundstrom marked this conversation as resolved.
Show resolved
Hide resolved
|
||
defp keys(table) do | ||
keys(table, :ets.first(table), []) | ||
end | ||
|
||
defp keys(_table, :"$end_of_table", acc), do: MapSet.new(acc) | ||
|
||
defp keys(table, key, acc) do | ||
keys(table, :ets.next(table, key), [key | acc]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
defmodule ETSTest do | ||
use ExUnit.Case | ||
alias Engine.ETS | ||
|
||
# Helper to create a temporary ETS table | ||
defp create_table do | ||
:ets.new(:test_table, [:named_table, :set, :public, {:keypos, 1}]) | ||
end | ||
|
||
setup do | ||
# Create a fresh table before each test | ||
table = create_table() | ||
{:ok, table: table} | ||
end | ||
|
||
test "inserts single tuple into an empty table", %{table: table} do | ||
# Act | ||
ETS.replace_contents(table, {:key1, "value1"}) | ||
|
||
# Assert | ||
assert {:key1, "value1"} in :ets.tab2list(table) | ||
end | ||
|
||
test "inserts a single entry when new entry is a tuple", %{table: table} do | ||
# Act | ||
ETS.replace_contents(table, {:key1, "value1"}) | ||
|
||
# Assert | ||
assert {:key1, "value1"} in :ets.tab2list(table) | ||
end | ||
|
||
test "inserts multiple entries when new entries is a list of tuples", %{table: table} do | ||
# Act | ||
ETS.replace_contents(table, [{:key1, "value1"}, {:key2, "value2"}, {:key3, "value3"}]) | ||
|
||
# Assert | ||
assert {:key1, "value1"} in :ets.tab2list(table) | ||
assert {:key2, "value2"} in :ets.tab2list(table) | ||
assert {:key3, "value3"} in :ets.tab2list(table) | ||
end | ||
|
||
test "replaces existing entries with new ones", %{table: table} do | ||
# Arrange | ||
:ets.insert(table, {:key1, "old_value1"}) | ||
:ets.insert(table, {:key2, "old_value2"}) | ||
|
||
# Act | ||
ETS.replace_contents(table, [{:key1, "new_value1"}, {:key3, "new_value3"}]) | ||
|
||
# Assert | ||
assert {:key1, "new_value1"} in :ets.tab2list(table) | ||
assert {:key3, "new_value3"} in :ets.tab2list(table) | ||
refute {:key2, "old_value2"} in :ets.tab2list(table) | ||
end | ||
|
||
test "removes keys that are not in the new entries", %{table: table} do | ||
# Arrange | ||
:ets.insert(table, {:key1, "old_value1"}) | ||
:ets.insert(table, {:key2, "old_value2"}) | ||
|
||
# Act | ||
ETS.replace_contents(table, [{:key1, "new_value1"}]) | ||
|
||
# Assert | ||
assert {:key1, "new_value1"} in :ets.tab2list(table) | ||
refute {:key2, "old_value2"} in :ets.tab2list(table) | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Also thought about naming this module
Engine.Cache
since ETS is acting as our cache.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.
Hmm yeah interesting thought - it's true that ETS essentially acts as our cache, although I'm not sure how I feel about abstracting it as such. It could help the cleanliness of some of the engine logic if we abstracted away the direct calls to :ets I suppose. Curious to see if @panentheos has thoughts
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 agree with Paul, I'm not sure there's much to be gained from adding new terminology here. Also, we usually use the name
Engine
to describe GenServer processes that are actively doing something, like polling data repeatedly. Since these are just functions, I'd probably file them under something with "Utilities" in the name, or just add them to one of the existing utility modules if they fit there.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.
Gotcha, that distinction for engines makes sense to me! Given the other common use cases you pointed out, I think having this as an
ETSUtilities
module works well