1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
//! Event sourcing support for Krill.
//!
//! This module implements an event sourcing framework for use specifically
//! with Krill.
//!
//! # Event Sourcing Overview
//!
//! ## The Audit is the Truth
//!
//! Event sourcing is a technique based on the concept that the current state
//! of an entity is based on a full record of past events – which can be
//! replayed – rather than saving and loading the state using something like
//! object-relational mapping or serialization.
//!
//! A commonly used example to explain this concept is bookkeeping. In
//! bookkeeping one tracks all financial transactions, like money coming in
//! and going out of an account. The current state of an account is determined
//! by the culmination of all these changes over time. Using this approach,
//! and assuming that no one altered the historical records, we can be 100%
//! certain that the current state reflects reality. Conveniently, we also
//! have a full audit trail which can be shown.
//!
//!
//! ## CQRS and Domain Driven Design
//!
//! Event sourcing is often combined with CQRS: Command Query Responsibility
//! Segregation. The idea here is that there is a separation between intended
//! changes (commands) sent to your entity, the internal state of the entity,
//! and its external representation.
//!
//! Separating these concerns we can also borrow heavily from
//! ["Domain Driven Design"](https://en.wikipedia.org/wiki/Domain-driven_design). In DDD structures are organized into so-called “aggregates”
//! of related structures. At their root they are joined by an
//! “aggregate root”.
//!
//!
//! ## Commands, Events and Data
//!
//! This separation means that the aggregate is a bit like a black box to the
//! outside world, but in a positive sense. Users of the code just need to
//! know what messages of intent – commands – they can send to it. This
//! interface, like an API, should be fairly stable.
//!
//! The first thing to do when a command is sent is that an aggregate is
//! retrieved from storage, so that it can receive a command. The state of
//! the aggregate is the result of all past events – but, because this can
//! get slow, aggregate snapshots are often used for efficiency. If a
//! snapshot does not include the latest events, then they are simply
//! re-applied to it.
//!
//! When an aggregate receives a command it can see if a change can be
//! applied. It is fully in charge of its own consistency. If it isn't then
//! the aggregate root is usually at the wrong level. The result of the
//! command can either be an error – the command is rejected – or a number
//! of events representing the state changes are applied to the aggregate.
//!
//! When events are applied they are also saved, so that they can be replayed
//! later. Furthermore, implementations often use message queues where events
//! are posted as well. This allows other components in the code to be
//! triggered by changes in an aggregate.
//!
//! In its purest form, these messages can then be used to (re-) generate
//! one or more data representations that users can *query*. E.g., you could
//! populate data tables in a SQL database. However, we are currently not
//! doing this.
//!
//! This combination of techniques has been championed by various people,
//! most notably Greg Young and Martin Fowler.
//!
//!
//! # Event Sourcing in Krill
//!
//! If you search for it you will find that there are many blog posts about
//! how people made event sourcing and CQRS work for their project. There are
//! also many libraries, for many languages, that provide frameworks. Some of
//! these libraries will have you believe that their implementation is the
//! **right** way of doing this – but we believe that there is more than one
//! way to happiness. So, in Krill we have chosen to use our own local
//! implementation. This lets us use the concepts that we need, but it allows
//! us the flexibility to adapt it to our specific needs, and avoids the
//! complication from concepts that we do not need.
//!
//! This module is Krill’s own event sourcing library code. In the following,
//! we will describe the concepts and code and include code (snippets).
//!
//! For an example use of the framework, see the `test` sub-module of this
//! module.
//!
//!
//! ## `KeyValueStore` and JSON
//!
//! Before we delve in to the Krill event sourcing code, we should talk a bit
//! about storage. Krill stores all values in a key-value store. This is
//! implemented in the [`commons::storage`][crate::commons::storage]
//! module.
//!
//! The store is agnostic of specific storage backends. Currently, only disk
//! and memory (for testing) implementations are supported. Database
//! options will be added in future.
//!
//! Opt-in locking on disk relies on the `fd-lock` crate This locking is used
//! by Krill to ensure that updates to CAs, the publication server access
//! and publication server content are always applied sequentially.
//!
//!
//! ## Aggregate
//!
//! As said, the aggregate is a central concept in DDD that is used in
//! combination with event sourcing and CQRS. Officially the root entity
//! should be called “aggregate root,” but because this a bit wordy Krill
//! just calls it 'Aggregate' instead. The trait [`Aggregate`] is defined for
//! this that is implemented by, for instance,
//! [`CertAuth`][crate::ca::CertAuth] and
//! [`RepositoryAccess`][crate::pubd::RepositoryAccess].
//!
//! As we will see further down, when we get to describe the
//! [`AggregateStore<A>`] which ties all of this together. This allows us to
//! focus on business logic in our aggregates.
//!
//! For each aggregate, you need to your command and Event types, and
//! implement the [`Aggregate::process_command`] method that processes a
//! command. The result should be either an error, or `Vec<Event>`. In case
//! the command is a no-op, return an empty `Vec`. Furthermore implement
//! [`Aggregate::apply`] and let your aggregate state be updated with event
//! data.
//!
//!
//! ## Commands and Storable Details
//!
//! As can be seen in the trait declaration, Krill expects that an
//! [`Aggregate`] defines a type that it will use for commands. The command
//! *must* implement the [`Command`] trait.
//!
//! When storing a command in the audit log, we may want to remove secret
//! data or, more likely, ephemeral things that cannot and should not be
//! saved. The trait [`WithStorableDetails`] takes care of producing a
//! command summary that can be stored. This summary is serialized into
//! a [`StoredCommand`] which contains some accounting information as well
//! as the command summary.
//!
//! It also contains the [`StoredEffect`], which tells us whether the
//! command was successful or not with more details for each case.
//!
//!
//! ## Events
//!
//! As mentioned above an [`Aggregate`] must specify which type of event it
//! uses. Events must implement the [`Event`] trait. They are stored in
//! a [`StoredCommand`] structure.
//!
//! > **Important:** We can change the internal implementation of an
//! > [`Aggregate`] pretty freely. As long as we keep backwards compatibility
//! > with regards to past events, we can just delete the snapshots and
//! > rebuild the current state based on past events. It is of course possible
//! > to add code to parse all > past commands and events, and reformat them.
//! > But, it's a major amount of work to do so, so it's better to avoid this
//! > if we can. Also note, that we can never add new information to past
//! > events. They already happened, so we have to deal with that.
//!
//!
//! ## Hybrid Model
//!
//! Before Krill version 0.9 *all* CA and Publication Server functions were
//! implemented in aggregates and used event sourcing. However, CAs in the
//! RPKI have to re-publish CRLs and Manifests very often. Even when
//! there are no changes to ROAs these objects must be refreshed before they
//! go stale. By default Krill re-publishes every! 16 hours.
//!
//! This resulted in an enormous amount of commands and events to be
//! generated for CAs, for a change that is done automatically – and which
//! does not reflect any semantic change for a CA. Keeping all this stuff in
//! the history led to excessive disk usage, and an audit trail that is hard
//! to follow. It also has a serious impact on rebuilding state based on
//! events from scratch, i.e., without using snapshots.
//!
//! Therefore we decided to implement a hybrid model in Krill. The Krill
//! [`CertAuth`][crate::ca::CertAuth] is still in charge of *almost*
//! all changes, and in particular all *semantic* changes that users made.
//! But, the generation of Manifests and CRLs is offloaded to an associated
//! component [`CaObjects`][crate::ca::publishing::CaObjects] that just keeps
//! the latest Manifest and CRL. It can re-sign these because it has access
//! to a [`KrillSigner`][crate::commons::crypto::KrillSigner] and it can get
//! the public key identifier needed for signing from the `CertAuth`.
//!
//! This is relevant here, because under the hood we use a
//! [`PreSaveEventListener`] to ensure that a new Manifest and CRL are
//! written when there is a change in ROAs or issued certificates, observed
//! in events.
//!
//!
//! ## Event Listeners
//!
//! The Krill event sourcing stack defines two different event listener
//! traits which are called by the [`AggregateStore`] when an aggregate is
//! successfully updated. The first, [`PreSaveEventListener`] is called
//! before updates are saved, and it can fail, the second,
//! [`PostSaveEventListener`] is called after all changes have been applied,
//! and thus cannot fail.
//!
//! In a nutshell, we use the event listeners for two things:
//!
//! * a pre-save trigger that the
//! [`CaObjects`][crate::ca::publishing::CaObjects]
//! for a CA gets an updated Manifest and CRL, and
//! * triggers that follow-up tasks are put on the scheduler, based on events.
//!
//! As discussed in issue
//! [1182](https://github.com/NLnetLabs/krill/issues/1182), it would be best
//! to remove the `PreSaveEventListener` trait and do everything through
//! (idempotent) triggered tasks on the queue in the scheduler. Note that
//! Krill will add any missing tasks on this queue at startup, so this means
//! that even if an aggregate, like a CA, is saved and then the follow-up
//! task scheduling fails because of an outage, the task will simply be
//! re-added when Krill restarts.
//!
//!
//! ## Aggregate Store
//!
//! When Krill interacts with an aggregate, it does so through an
//! [`AggregateStore<A>`]. The aggregate store provides a convenient access
//! layer to dealing with any aggregate. Using this lets you focus on pure
//! business logic in your aggregates. Essentially, we just need to implement
//! the [`Aggregate`], [`Command`] and [`Event`] traits and then the
//! framework will deal with storage concerns.
//!
//! The most important pub function signatures (implementation omitted):
//!
//! ### Applying changes
//!
//! All changes to aggregates are done through the
//! [`command`][AggregateStore::command] function. This function waits for a
//! lock to ensure that all changes to the Aggregate are applied
//! sequentially.
//!
//! First the aggregate is retrieved from the in memory cache if present. If
//! there is no cached instance then latest snapshot is retrieved from
//! storage instead. If there is also no snapshot, then the init command
//! (i.e. with version 0) is retrieved and applied instead. Then the key
//! value store is queried for follow-up [`StoredCommand`] values (for the
//! version of the aggregate) which are then applied.
//!
//! Once the latest aggregate has been retrieved, the command is sent to it.
//! Note that this command is not a [`StoredCommand`]. It contains the intent
//! for a change, while [`StoredCommand`] contains the result of such a
//! change. The aggregate is responsible for verifying the command and it can
//! return with: an error, no effect, or a vec of change events.
//!
//! If a command has no effect, i.e. there is no error and no change, then it
//! is simply forgotten.
//!
//! If a command has an effect then a [`StoredCommand`] is created that
//! contains a storable representation of the command (e.g. certain values in
//! a command, like an `Arc<Signer>`, or `Arc<Config>` are not included) and
//! either the error or a vec of events. This stored command gets a unique
//! key name based on the version of the aggregate that it affects.
//!
//! If there should be an existing key-value pair for this stored command,
//! then this indicates that the locking mechanism failed somehow. This
//! should not happen, but if it did, then the command is NOT saved. Instead
//! Krill exits with an error message.
//!
//! But if all is well and as expected, the command with events is applied
//! to the aggregate. Note that this just updates its version in case of a
//! command that resulted in an error. The in-memory cached aggregate is
//! updated to help performance when retrieving it later. Then the command
//! is saved.
//!
//! ### Retrieving
//!
//! When retrieving an aggregate for read access, Krill actually follows
//! the same code path that is used for applying a command, except that in
//! this case the underlying function that takes care of locking and
//! retrieving the latest Aggregate is called with `None` instead of
//! `Some(command)`, so it simply returns without trying to apply any
//! changes. This ensures however, that the same locking rules are observed
//! in both cases and allows us to avoid code duplication.
//!
//! ### Snapshots
//!
//! Note that we do not update the full snapshot on every change because this
//! could create a performance issue in cases where aggregates are big (e.g.,
//! a CA with many children or objects) and serialization takes a long time.
//! Instead, updating the snapshots is done daily through a different code
//! path from the scheduler. See `Task::UpdateSnapshots`. This code, again,
//! actually uses the same underlying function as above to retrieve the
//! snapshot, this time setting the `save_snapshot` parameter to true to
//! ensure that the snapshot is saved.
//!
//!
//! ## WAL Store
//!
//! The [`WalStore`] – WAL being short for write-ahead log – is a simplified
//! version of the full aggregate store. It doesn’t store the full command
//! history of an instance but only the changes since the last snapshot was
//! created. Listeners aren’t supported either.
pub use ;
pub use ;
pub use ;