Architecture

Architecture Overview

Listeners

Events-eiffel plugin listens to ref-updated for refs/heads/* to create SCS events, ref-updated for refs/tags/* to create tag events and patchset-created to create SCC.

You can disable event-triggering or configure which refs event creation is triggered for in the All-Project configuration.

REST API

The plugin sports a REST API to create missing events manually, also useful if you want to do a “backfill” of historical commits in a controlled fashion before enabling the event-triggered creation of Eiffel events:

The REST APIs are not protected by the same ref-filter that the listeners are, so it should be used with caution. For this reason the REST API endpoints requires the caller to have the ADMINISTRATE_SERVER capability.

Parse queue

Since each parse request potentially can result in walking hundreds of thousand commits and create twice as many Eiffel events (a new branch in the linux kernel would result in ~1.6m events). We need to limit the effect it has on the Gerrit server. The plugin creates it‘s own work queue for incoming parse-requests. Queued parse-requests can be monitored through Gerrit’s show-queue command.

Number of executers used to consume this queue can be configured with EventParsing.poolSize. A reasonable poolsize for a large Gerrit instance should be between 2-4.

Event creation

Parsing

For each Eiffel event creation request first we need to ensure that all the event's parents are created. To find missing parent events we need to walk the commit graph until we find a commit that has a corresponding event of the correct type.

Parsing is done with custom commit-walkers src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/CommitsWalker.java.

These custom commit-walkers use an external state (whether the corresponding Eiffel event is already created or not) to determine when to stop walking. This state is fetched from the EiffelEventHub.

They also make sure to reuse the object database when walking for child events. E.g. when parsing for SCS event creation we must first parse for corresponding SCC as they must be created first. Creating and tearing down an object database once for each event type is computationally costly and uses twice as much memory.

Mapping

Once we have determined which commits we need to create events for we need to map those commits to an Eiffel event.

com/googlesource/gerrit/plugins/eventseiffel/mapping/EiffelEventMapper.java

You can configure the mapping get values from the commit instead of using the index to find change-data to populate the Eiffel event from by setting EventMapping.useIndex to False. This gives a noticeable performance increase when doing a backfill of historical events.

For tag-events (Artc and CD) you can set a namespace by configuring EiffelEvent.namespace. This namespace becomes part of the purl of the Artc data.identity and the data.name of the CD event. See example at: Tag representation (in the example namespace is set to gerrit-host).

Publishing

Event hub

com/googlesource/gerrit/plugins/eventseiffel/EiffelEventHub.java

The event hub has four different functions:

  • As an API for querying of existing Eiffel events and their properties.
  • Keeping track of the state of the Eiffel events.
  • Maintaining the queue containing the Eiffel events that should be published.
  • Updating the local EiffelEventIdCache once an event is reported as published and removed from the queue.
  • Handling the lifecycle of the Consumer that feeds events to the Publisher.

The thread-safe implementation com/googlesource/gerrit/plugins/eventseiffel/EiffelEventHubImpl.java is loosely modeled around LinkedBlockingQueue.java adopted for the more complicated data model of Eiffel events.

Publish event worker

com/googlesource/gerrit/plugins/eventseiffel/PublishEventWorker.java

The responsibility of the PublishEventWorker is to take events from the queue and feed them to the EventPublisher, which in turn is responsible for publishing the events to Eiffel, as well as keeping track of if the publishing was successful

and report back to the Event Hub.

Event Publisher

There is currently only one implementation of EiffelEventPublisher. This implementation publishes the event to a RabbitMQ exchange: com/googlesource/gerrit/plugins/eventseiffel/mq/RabbitMqPublisher.java

Event ID Cache

com/googlesource/gerrit/plugins/eventseiffel/cache/EiffelEventIdCacheImpl.java

The cache uses a persisted LoadingCache and is backed by the implementation of com/googlesource/gerrit/plugins/eventseiffel/eiffel/api/EventStorage.java Currently the only implementation of EventStorage uses: Eiffel GraphQL API and Eiffel GoER.

Extendability

Event Storage

If you want to use a different EventStorage, e.g. a local DBMS like postgres or similar, you will need to

  • Write a Postgres implementation of the EventStorage interface.
  • Write a Provider for the implementation.
  • Add configuration for this event storage and ensure that EventStorage is be bound to that Provider when configured.

Publish to Eiffel

If you use other means than a RabbitMQ exchange for publishing events to Eiffel, e.g. REMRem or similar, you will need to:

  • Write a REMRem implementation of the com/googlesource/gerrit/plugins/eventseiffel/eiffel/api/EiffelEventPublisher.java interface.
  • Add configuration for this event publisher and ensure that EiffelEventPublisher is bound to the REMRem implementation when configured for it.