Open-Source Wikis

/

GitLab

/

Systems

/

EventStore

gitlab-org/gitlab

EventStore

A pub/sub layer built on Sidekiq for decoupling domain events from their consumers.

Purpose

Producers publish small, schema-validated events. Multiple subscribers react asynchronously without the producer knowing about them. The pattern keeps services from accumulating after-effect chains.

Source

Concern Location
Core API lib/gitlab/event_store.rb
Subscriber concern lib/gitlab/event_store/subscriber.rb
Event base class lib/gitlab/event_store/event.rb
Initializer (subscription wiring) config/initializers/event_store.rb
Event definitions app/events/ and ee/app/events/ (~15 directories)

Defining an event

module Projects
  class ProjectCreatedEvent < Gitlab::EventStore::Event
    def schema
      {
        type: 'object',
        required: %w[project_id],
        properties: {
          project_id: { type: 'integer' }
        }
      }
    end
  end
end

Generated via bin/rails generate event Projects/ProjectCreated project_id:integer.

Publishing

Gitlab::EventStore.publish(
  Projects::ProjectCreatedEvent.new(data: { project_id: project.id })
)

Publish happens synchronously in the request transaction. The schema is validated before enqueueing.

Subscribing

class Onboarding::ProjectCreatedWorker
  include ApplicationWorker
  include Gitlab::EventStore::Subscriber

  feature_category :onboarding
  idempotent!

  def handle_event(event)
    # event.data is a hash matching the schema
  end
end

Wire it in the initializer:

# config/initializers/event_store.rb
Gitlab::EventStore.subscribe(
  Onboarding::ProjectCreatedWorker,
  to: Projects::ProjectCreatedEvent
)

A worker can subscribe with a if: block for filtering:

Gitlab::EventStore.subscribe(
  FooWorker,
  to: Issues::IssueClosedEvent,
  if: ->(event) { event.data[:weight].present? }
)

Delivery semantics

  • Each subscriber receives its own Sidekiq job (one event → N enqueued jobs).
  • Idempotent and deduplicated by default (subscribers must be idempotent).
  • Schema mismatch raises in development/test, logs in production.
  • The event is enqueued only after the database transaction commits (via after_commit); failed transactions don't fire events.

When to use

  • Cross-domain notifications (project created → onboarding wakes up; issue closed → analytics reads it).
  • Avoid orchestrators with MyService.new(...).execute then OtherWorker.perform_async then ....

When not to use

  • Synchronous business logic — call the service directly.
  • Tight feedback loops where the consumer must finish before the producer responds.
  • Things that need ordering guarantees across events — Sidekiq doesn't preserve order.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

EventStore – GitLab wiki | Factory