Conversation
44a3512 to
f5791bb
Compare
| end | ||
|
|
||
| describe "match/1" do | ||
| test "can return facility alerts" do |
There was a problem hiding this comment.
the order of matchers shouldn't matter anyway here since they are ALL being applied (in other words, all matchers are logically OR'd together like trip_id == 1234 AND stop_id == nil OR trip_id == nil AND stop_id == 1234... ad. inf.) and the set isn't ordered
| facility: facility | ||
| } | ||
| facility <- part_values(matcher, :facility), | ||
| reduce: MapSet.new() do |
There was a problem hiding this comment.
Instead of reduce, I think you can use uniq: true. https://elixir.hexdocs.pm/Kernel.SpecialForms.html#for/1-the-into-and-uniq-options
Though see my next comment; I think you want to unique-ify things at a higher call level.
| } | ||
| facility <- part_values(matcher, :facility), | ||
| reduce: MapSet.new() do | ||
| # we create one nil selector for each parameter that's passed in |
There was a problem hiding this comment.
I don't see how the duplication you've described is happening here. IIUC, part_values() returns at worst a two-element list with nil as the second element. Since there are no duplicated nils in any one list, then there is no duplication in the cartesian product of the generators, and so no reason to unique-ify here.
There was a problem hiding this comment.
good catch on this! I originally was doing Enum.uniq\1 in the match function, but I decided to get cute and evidently ended up doing it in the wrong place. 5fb6ab0
There was a problem hiding this comment.
This change the changes in the tests redundant
e269be6 to
d65f2be
Compare
|
Load test results. master/prod: feature/dev-green: |
| # we create one nil selector for each parameter that's passed in | ||
| # so when a list is passed in, there is a nil selector for every item in the list | ||
| # meaning with a list length of N there are N - 1 redundant selectors) | ||
| # hence the use of a map set to remove duplicates here |
There was a problem hiding this comment.
Please update this comment; you're no longer removing duplicates. In fact I think you could revert the whole comment entirely, or perhaps adapt it to the comprehension on line 19.
| @@ -32,27 +32,34 @@ defmodule State.Alert.InformedEntityTest do | |||
|
|
|||
| describe "match/1" do | |||
There was a problem hiding this comment.
Is there any way to create a test that would only pass with your change in place?
There was a problem hiding this comment.
I don't think we can easily do this without leaking some of the private details of the InformedEntiy module. I think the more important thing to test (performance) is non-functional here, and is validated by the load tests
|
|
||
| def new(table \\ @table) do | ||
| ^table = :ets.new(table, [:named_table, :duplicate_bag, {:read_concurrency, true}]) | ||
| # keypos 2 here sets the key for the ETS table to be the alert ID, which makes selecting by alert ID faster |
There was a problem hiding this comment.
Nit: please wrap at 100 characters
| # we create one nil selector for each parameter that's passed in | ||
| # so when a list is passed in, there is a nil selector for every item in the list | ||
| # meaning with a list length of N there are N - 1 redundant selectors) | ||
| # hence the use of a map set to remove duplicates here |
There was a problem hiding this comment.
s/a map set to remove duplicates/uniq/
| uniq: true do | ||
| # we create one nil selector for each parameter that's passed in | ||
| # so when a list is passed in, there is a nil selector for every item in the list | ||
| # meaning with a list length of N there are N - 1 redundant selectors) |
There was a problem hiding this comment.
Please fix dangling close-paren
34754fb to
53c7907
Compare
53c7907 to
6b92efa
Compare
| # We create one nil selector for each combination in the Cartesian product of | ||
| # parameters that are passed in, with one `nil` for each element in each list. For example, | ||
| # with stops 1, 2 and trips 1, 2 we get all of these combinations including nil: | ||
| # [(1, nil), (1, nil), (2, nil), (2, nil), (nil, 1), (nil, 1), (nil, 2) (nil, 2)]. |
There was a problem hiding this comment.
Nit: I understand this comment but I think if I hadn't been reviewing this PR, it would confuse me. A small change would clarify: "stops 1, 2 and trips A, B".
There was a problem hiding this comment.
Also, wouldn't we get an additional four copies of a (nil, nil) combination?
Summary of changes
Asana Tecket: 🍎 Figure out what went wrong with removing alerts hook
Problem:
Long, meandering Slack thread in which I crash out here: https://mbta.slack.com/archives/C06D663HRRN/p1790722806365799
The main performance problem boils down to the fact that
nilgets repeated in theState.Alert.InformedEntitymatchers for each element in a list.For example, if you pass filter[stop_id]=1,2,3,4&filter[trip_id]=1,2,3,4 you end up with matchers for the collections [1, nil, 2, nil, 3, nil, 4, nil] * (cartesian product) 1,[nil, 2, nil, 3, nil, 4, nil]. So you get 8 * 8 = 64 matchers instead of what you want, which is [1, 2, 3, 4, nil] * [1, 2, 3, 4, nil] or 25 matchers.
Solution:
Make the matchers unique. This is only a linear improvement in runtime with respect to number of items in the list filters. I expect that you could get an exponential improvement (e.g. O(n^2) matchers -> O(1) matchers) if you used
:ets.fun2msorEx2msto generate one matcher that satisfies all conditions.Also, I set the key for the
InformedEntityETS table to be the alert ID, which I think will result in more efficient in-memory representation of the table. The old key was the name of the struct (ETS defaults to using the first item in the tuple as the key), which obviously is not unique.For future work, I expect that you could get the growth of matchers with respect to input list items from O(n^2) to O(1) if you used
:ets.fun2ms/1orEx2msto generated a compound match expression that covers all of the criteria instead of trying to create many matchers.🤖 AI Disclosure: I was banging my head against the wall on this and used copilot to point me at the right line. I already knew basically what file the problem was in, and what category of problem it was. So I pointed Copilot at
informed_entity.exand asked for ideas on what could degrade ETS performance. Then I profiled some of the suggestions. The code written is all my own.--
Stack created with GitHub Stacks CLI • Give Feedback 💬