diff --git a/database.toml b/database.toml --- a/database.toml +++ b/database.toml @@ -11,3 +11,7 @@ [instances.concurrent-append-no-fail-if.seeds.schema] type = "sql-file" path = "db/schema/0.1.0.sql" + +[instances.concurrent-append-conflicting-tags.seeds.schema] +type = "sql-file" +path = "db/schema/0.1.0.sql" diff --git a/lib/en57/benchmark.rb b/lib/en57/benchmark.rb --- a/lib/en57/benchmark.rb +++ b/lib/en57/benchmark.rb @@ -144,6 +144,16 @@ batch_size: 100, ) end, + "concurrent-append-conflicting-tags" => ->(database_url, measure) do + ConcurrentAppendConflictingTags.new( + name: "Concurrent append, conflicting tags", + database_url:, + measure:, + runs: ENV.fetch("BENCHMARK_RUNS", 1), + concurrency: 10, + batch_size: 100, + ) + end, }, ) end @@ -194,7 +204,6 @@ concurrently(@concurrency) do tags = %W[writer:#{SecureRandom.hex(4)}] - scope = @event_store.read.of_type(type).with_tag(tags) events = Array.new(@batch_size) { En57::Event.new(type: type, tags: tags) } @@ -239,6 +248,43 @@ begin @event_store.append(events, fail_if: scope.after(position = 0)) rescue AppendConditionViolated + retry + end + end + end + end + + def verify = + @event_store.read.each.to_a.size == @runs * @concurrency * @batch_size + end + + class ConcurrentAppendConflictingTags < Scenario + def initialize(...) + super + @event_store = + EventStore.for_pooled_pg(@database_url, max_connections: @concurrency) + end + + private + + def call + type = "event_benchmarked" + tags = %W[writer:#{SecureRandom.hex(4)}] + barrier = Concurrent::CyclicBarrier.new(@concurrency) + + concurrently(@concurrency) do + scope = @event_store.read.of_type(type).with_tag(tags) + events = + Array.new(@batch_size) { En57::Event.new(type: type, tags: tags) } + position = 0 + + barrier.wait + + @measure.call do + begin + @event_store.append(events, fail_if: scope.after(position)) + rescue AppendConditionViolated + scope.each_with_position { |_event, event_position| position = event_position } retry end end