Add batching to RedisCounter to reduce Redis writes

What does this MR do and why?

See #497665 (closed) for motivation.

This MR provides an implementation of RedisCounter which minimizes the number writes to Redis. A constant number (<2) of Redis operations will be used for each redis_key instead a constant number of operations per update.

This MR also refactors AgentHelpers to use the batched approach. This is remedy the problems described in #497665 (closed).

Redis pipelines were considered, but would stil cause a linear number of Redis writes. This change results in a constant number of Redis writes (assuming a constant number of distinct Redis keys are updated).

In addition, this solution is much faster than Redis Pipelines for a large number of updates. For n=200_000, a modified version of A real world code example gives the following output:

without pipelining 4.650433 seconds
with pipelining 0.750922 seconds
with batching 0.000118 seconds

Notice: Only Redis counters and not RedisHLL counter are addressed in this MR. Something similar can be done for RedisHLL counters, but we would need to keep the unique values in memory.

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

Before After

How to set up and validate locally

To test RedisCounter

  1. Setup tracing:
    TracePoint.new(:call) do |tp|
      if tp.defined_class == Redis && tp.method_id == :send_command
        puts "Called: #{tp.binding.local_variable_get(:command)}"
      end
    end.enable
  2. Update a RedisCounter 10 times:
    10.times { Gitlab::InternalEvents.track_event('create_flux_git_push_notification') }
  3. Try again but with batched writes:
    Gitlab::InternalEvents.with_batched_redis_writes { 10.times { Gitlab::InternalEvents.track_event('create_flux_git_push_notification') }}

Notice how only the minimal number of calls to Redis is executed with using with_batched_redis_writes.

Numbered steps to set up and validate the change are strongly suggested.

Edited by Jonas Larsen

Merge request reports

Loading