blob: 4c38500c160b53ec1b60e4a3ef542186a805ebf0 [file] [log] [blame] [view]
# Architecture
![Architecture Overview](./images/architecture.png)
## 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](./config-project-level.md).
## 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:
* [POST SCC](./rest-api-events-eiffel-scc-post.md)
* [POST SCS](./rest-api-events-eiffel-scs-post.md)
* [POST ArtC](./rest-api-events-eiffel-artc-post.md)
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](../../../Documentation/cmd-show-queue.html).
Number of executers used to consume this queue can be configured with
[EventParsing.poolSize](../config.md).
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](./basic-principle.md#identifying-missing-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](#event-hub).
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](../config.md) 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](./config.md). This `namespace` becomes part of the purl
of the Artc
[data.identity](https://github.com/eiffel-community/eiffel/blob/edition-arica/eiffel-vocabulary/EiffelArtifactCreatedEvent.md#dataidentity)
and the
[data.name](https://github.com/eiffel-community/eiffel/blob/edition-arica/eiffel-vocabulary/EiffelCompositionDefinedEvent.md#dataname)
of the CD event. See example at:
[Tag representation](./basic-principle.md#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](./basic-principle.md#consistency-vs-performance)
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](https://github.com/eiffel-community/eiffel-graphql-api)
and [Eiffel GoER](https://github.com/eiffel-community/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<EventStorage>](https://google.github.io/guice/api-docs/3.0/javadoc/com/google/inject/Provider.html)
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](https://github.com/eiffel-community/eiffel-remremhttps://github.com/eiffel-community/eiffel-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.