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

Replace Hammer for rate limiting with custom ets bucket #3571

Merged
merged 1 commit into from
Dec 6, 2023

Conversation

ruslandoga
Copy link
Contributor

@ruslandoga ruslandoga commented Nov 30, 2023

Changes

This PR replaces Hammer with a custom wrapper around ETS due to Hammer being prone to overload: ExHammer/hammer#71

I'm opening this PR for two reasons:

  • there doesn't seem to be any benefit from using Hammer (and its choice of backends) since Plausible uses only the default ETS backend
  • it's not yet clear when or if new ets ExHammer/hammer#72 would get merged

Tests

  • Automated tests have been added

Changelog

  • This PR does not make a user-facing change

Documentation

  • This change does not need a documentation update

Dark mode

  • This PR does not change the UI

@ruslandoga ruslandoga requested a review from a team November 30, 2023 11:06
@ruslandoga ruslandoga force-pushed the rm-hammer branch 6 times, most recently from d74c955 to 9f5c573 Compare December 1, 2023 14:33
@aerosol aerosol changed the title rm Hammer Replace Hammer for rate limiting with custom ets bucket Dec 5, 2023
@aerosol aerosol mentioned this pull request Dec 6, 2023
@aerosol
Copy link
Member

aerosol commented Dec 6, 2023

@ruslandoga could you rebase/merge master as in #3595 ?

@ruslandoga
Copy link
Contributor Author

@aerosol done ❤️

{:read_concurrency, true},
{:write_concurrency, true},
{:decentralized_counters, true}
])
Copy link
Member

Choose a reason for hiding this comment

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

I'm looking at how Hammer's table is configured:

[
  id: #Reference<0.563007245.2435186714.162753>,
  decentralized_counters: false,
  read_concurrency: false,
  write_concurrency: false,
  compressed: false,
  memory: 7351569,
  owner: #PID<0.3706.0>,
  heir: :none,
  name: :hammer_ets_buckets,
  size: 389610,
  node: :"plausible@plausible-app-01",
  named_table: true,
  type: :ordered_set,
  keypos: 1,
  protection: :public
]

I'm sure you've benchmarked the hell out of it, what was the difference between using set and ordered_set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've only benchmarked different implementations https://github.com/ruslandoga/rate_limit, and I haven't benchmarked set vs ordered-set directly. I'll do a benchmark now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

$ env MIX_ENV=bench mix run bench/basic.exs
Operating System: macOS
CPU Information: Apple M1
Number of Available Cores: 8
Available memory: 8 GB
Elixir 1.15.7
Erlang 26.1.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 14 s

Benchmarking ordered_set ...
Benchmarking set ...

Name                  ips        average  deviation         median         99th %
set                5.20 M      192.26 ns ±15809.05%         166 ns         209 ns
ordered_set        3.73 M      268.42 ns ±12499.97%         209 ns         292 ns

Comparison: 
set                5.20 M
ordered_set        3.73 M - 1.40x slower +76.16 ns

Copy link
Contributor Author

Choose a reason for hiding this comment

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

def handle_info(:clean, state) do
clean(state.table)
schedule(state.clean_period)
{:noreply, state}
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to hibernate the process, since all it's doing is waiting another 10 minutes for the next cleanup?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't used process hibernation before. I'll read up on that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From a quick look, process hibernation forces a garbage collection (we have opts as garbage) and compacts memory usage. I think it wouldn't hurt, but at the same time the gains don't seem significant either (cleaning up opts and compacting a two-element map).

Copy link
Member

Choose a reason for hiding this comment

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

Buckle up, we're going live :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😵

@aerosol aerosol merged commit 0eedf9a into plausible:master Dec 6, 2023
@ruslandoga ruslandoga deleted the rm-hammer branch December 6, 2023 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants