diff --git a/.mutant.yml b/.mutant.yml index d1bdef2..936dd53 100644 --- a/.mutant.yml +++ b/.mutant.yml @@ -28,4 +28,5 @@ matcher: - En57::Benchmark::ConcurrentAppendConflictingTags* - En57::Benchmark::ConcurrentAppendNoFailIf* - En57::Benchmark::ConcurrentAppendNonConflictingTags* + - En57::Benchmark::ConcurrentAppendNonConflictingTagsSeeded* - En57::Benchmark::Scenario#concurrently # thread raise mutation survives despite direct coverage diff --git a/database.toml b/database.toml index 2102715..a91f5eb 100644 --- a/database.toml +++ b/database.toml @@ -8,6 +8,14 @@ path = "db/schema/0.1.0.sql" type = "sql-file" path = "db/schema/0.1.0.sql" +[instances.concurrent-append-non-conflicting-tags-seeded.seeds.schema] +type = "sql-file" +path = "db/schema/0.1.0.sql" + +[instances.concurrent-append-non-conflicting-tags-seeded.seeds.events] +type = "sql-file" +path = "db/seeds/concurrent_append_non_conflicting_tags_seeded.sql" + [instances.append-no-fail-if.seeds.schema] type = "sql-file" path = "db/schema/0.1.0.sql" diff --git a/db/seeds/concurrent_append_non_conflicting_tags_seeded.sql b/db/seeds/concurrent_append_non_conflicting_tags_seeded.sql new file mode 100644 index 0000000..da205ff --- /dev/null +++ b/db/seeds/concurrent_append_non_conflicting_tags_seeded.sql @@ -0,0 +1,18 @@ +-- Seeds 1,000,000 events, each carrying a single random "writer:<8 hex>" tag. +-- The tag shape mirrors ConcurrentAppendNonConflictingTagsSeeded's +-- `writer:#{SecureRandom.hex(4)}`, so the tag index is populated with values +-- of the same shape the benchmark queries. +INSERT INTO en57.events (id, type) +SELECT + gen_random_uuid (), + 'event_benchmarked' +FROM + generate_series(1, 1000000); + +INSERT INTO en57.tags (event_id, value) +SELECT + id, + 'writer:' || substr(md5(random()::text), 1, 8) +FROM + en57.events; + diff --git a/lib/benchmark/concurrent_append_non_conflicting_tags_seeded.rb b/lib/benchmark/concurrent_append_non_conflicting_tags_seeded.rb new file mode 100644 index 0000000..7aba247 --- /dev/null +++ b/lib/benchmark/concurrent_append_non_conflicting_tags_seeded.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module En57 + module Benchmark + class ConcurrentAppendNonConflictingTagsSeeded < Scenario + SEEDED_EVENTS = 1_000_000 + + def initialize(...) + super + @event_store = + EventStore.for_pooled_pg(@database_url, max_connections: @concurrency) + end + + private + + def call + type = "event_benchmarked" + barrier = Concurrent::CyclicBarrier.new(@concurrency) + + 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) } + + barrier.wait + + @measure.call do + begin + @event_store.append(events, fail_if: scope.after(position = 0)) + rescue AppendConditionViolated + record_retry + retry + end + end + end + end + + def verify = + @event_store.read.each.to_a.size == + SEEDED_EVENTS + total_runs * @concurrency * @batch_size + end + end +end diff --git a/lib/en57/benchmark.rb b/lib/en57/benchmark.rb index f3ac982..b9ee27b 100644 --- a/lib/en57/benchmark.rb +++ b/lib/en57/benchmark.rb @@ -162,13 +162,7 @@ module En57 def warmup = @warmup_runs.times { call } def concurrently(concurrency) - Array - .new(concurrency) do - Thread.new do - yield - end - end - .each(&:value) + Array.new(concurrency) { Thread.new { yield } }.each(&:value) end end @@ -176,6 +170,7 @@ module En57 require_relative "../benchmark/append_non_conflicting_tags" require_relative "../benchmark/concurrent_append_no_fail_if" require_relative "../benchmark/concurrent_append_non_conflicting_tags" + require_relative "../benchmark/concurrent_append_non_conflicting_tags_seeded" require_relative "../benchmark/concurrent_append_conflicting_tags" class Runner @@ -246,6 +241,21 @@ module En57 batch_size: 100, ) end, + "concurrent-append-non-conflicting-tags-seeded" => ->( + database_url, + warmup_runs, + measure + ) do + ConcurrentAppendNonConflictingTagsSeeded.new( + name: "Concurrent append, non-conflicting tags (seeded)", + database_url:, + measure:, + warmup_runs:, + runs:, + concurrency: 10, + batch_size: 100, + ) + end, "concurrent-append-conflicting-tags" => ->( database_url, warmup_runs, diff --git a/test/test_benchmark.rb b/test/test_benchmark.rb index 9a13042..f0c123b 100644 --- a/test/test_benchmark.rb +++ b/test/test_benchmark.rb @@ -340,6 +340,7 @@ module En57 append-non-conflicting-tags concurrent-append-no-fail-if concurrent-append-non-conflicting-tags + concurrent-append-non-conflicting-tags-seeded concurrent-append-conflicting-tags ], Runner.names, @@ -373,6 +374,13 @@ module En57 50, 10, ], + [ + "concurrent-append-non-conflicting-tags-seeded", + ConcurrentAppendNonConflictingTagsSeeded, + "Concurrent append, non-conflicting tags (seeded)", + 50, + 10, + ], [ "concurrent-append-conflicting-tags", ConcurrentAppendConflictingTags,