|
2 | 2 |
|
3 | 3 | Akka Persistence journal and snapshot store backed by PostgreSql database. |
4 | 4 |
|
5 | | -**WARNING: Akka.Persistence.PostgreSql plugin is still in beta and it's mechanics described below may be still subject to change**. |
6 | | - |
7 | | -### Setup |
8 | | - |
9 | | -To activate the journal plugin, add the following lines to actor system configuration file: |
10 | | - |
11 | | -``` |
12 | | -akka.persistence.journal.plugin = "akka.persistence.journal.postgresql" |
13 | | -akka.persistence.journal.postgresql.connection-string = "<database connection string>" |
14 | | -``` |
15 | | - |
16 | | -Similar configuration may be used to setup a PostgreSql snapshot store: |
17 | | - |
18 | | -``` |
19 | | -akka.persistence.snapshot-store.plugin = "akka.persistence.snapshot-store.postgresql" |
20 | | -akka.persistence.snapshot-store.postgresql.connection-string = "<database connection string>" |
21 | | -``` |
22 | | - |
23 | | -Remember that connection string must be provided separately to Journal and Snapshot Store. To finish setup simply initialize plugin using: `PostgreSqlPersistence.Init(actorSystem);` |
| 5 | +**WARNING: Akka.Persistence.PostgreSql plugin is still in beta and it's mechanics described bellow may be still subject to change**. |
24 | 6 |
|
25 | 7 | ### Configuration |
26 | 8 |
|
27 | 9 | Both journal and snapshot store share the same configuration keys (however they resides in separate scopes, so they are definied distinctly for either journal or snapshot store): |
28 | 10 |
|
29 | | -- `class` (string with fully qualified type name) - determines class to be used as a persistent journal. Default: *Akka.Persistence.PostgreSql.Journal.PostgreSqlJournal, Akka.Persistence.PostgreSql* (for journal) and *Akka.Persistence.PostgreSql.Snapshot.PostgreSqlSnapshotStore, Akka.Persistence.PostgreSql* (for snapshot store). |
30 | | -- `plugin-dispatcher` (string with configuration path) - describes a message dispatcher for persistent journal. Default: *akka.actor.default-dispatcher* |
31 | | -- `connection-string` - connection string used to access PostgreSql database. Default: *none*. |
32 | | -- `connection-string-name` - in case when connection-string is empty, this field specifies entry in the \*.config connection string section, from where connection string will be taken. Default: *none*. |
33 | | -- `connection-timeout` - timespan determining default connection timeouts on database-related operations. Default: *30s* |
34 | | -- `schema-name` - name of the database schema, where journal or snapshot store tables should be placed. Default: *public* |
35 | | -- `table-name` - name of the table used by either journal or snapshot store. Default: *event_journal* (for journal) or *snapshot_store* (for snapshot store) |
36 | | -- `auto-initialize` - flag determining if journal or snapshot store related tables should by automatically created when they have not been found in connected database. Default: *false* |
37 | | -- `timestamp-provider` (journal only) - type of the object used to generate journal event timestamps. Default: *Akka.Persistence.Sql.Common.Journal.DefaultTimestampProvider, Akka.Persistence.Sql.Common* |
38 | | - |
39 | | -### Custom SQL data queries |
40 | | - |
41 | | -PostgreSql persistence plugin defines a default table schema used for both journal and snapshot store. |
42 | | - |
43 | | -**EventJournal table**: |
44 | | - |
45 | | - +----------------+-------------+------------+---------------+---------+--------------------------+ |
46 | | - | persistence_id | sequence_nr | is_deleted | payload_type | payload | created_at | |
47 | | - +----------------+-------------+------------+---------------+---------+--------------------------+ |
48 | | - | varchar(200) | bigint | boolean | varchar(500) | bytea | timestamp with time zone | |
49 | | - +----------------+-------------+------------+---------------+---------+--------------------------+ |
50 | | - |
51 | | -**SnapshotStore table**: |
52 | | - |
53 | | - +----------------+--------------+--------------------------+------------------+---------------+----------+ |
54 | | - | persistence_id | sequence_nr | created_at | created_at_ticks | snapshot_type | snapshot | |
55 | | - +----------------+--------------+--------------------------+------------------+--------------------------+ |
56 | | - | varchar(200) | bigint | timestamp with time zone | smallint | varchar(500) | bytea | |
57 | | - +----------------+--------------+--------------------------+------------------+--------------------------+ |
| 11 | +Remember that connection string must be provided separately to Journal and Snapshot Store. |
| 12 | + |
| 13 | +```hocon |
| 14 | +akka.persistence{ |
| 15 | + journal { |
| 16 | + plugin = "akka.persistence.journal.postgresql" |
| 17 | + postgresql { |
| 18 | + # qualified type name of the PostgreSql persistence journal actor |
| 19 | + class = "Akka.Persistence.PostgreSql.Journal.PostgreSqlJournal, Akka.Persistence.PostgreSql" |
| 20 | +
|
| 21 | + # dispatcher used to drive journal actor |
| 22 | + plugin-dispatcher = "akka.actor.default-dispatcher" |
| 23 | +
|
| 24 | + # connection string used for database access |
| 25 | + connection-string = "" |
| 26 | +
|
| 27 | + # default SQL commands timeout |
| 28 | + connection-timeout = 30s |
| 29 | +
|
| 30 | + # PostgreSql schema name to table corresponding with persistent journal |
| 31 | + schema-name = public |
| 32 | +
|
| 33 | + # PostgreSql table corresponding with persistent journal |
| 34 | + table-name = event_journal |
| 35 | +
|
| 36 | + # should corresponding journal table be initialized automatically |
| 37 | + auto-initialize = off |
| 38 | + |
| 39 | + # timestamp provider used for generation of journal entries timestamps |
| 40 | + timestamp-provider = "Akka.Persistence.Sql.Common.Journal.DefaultTimestampProvider, Akka.Persistence.Sql.Common" |
| 41 | + |
| 42 | + # metadata table |
| 43 | + metadata-table-name = metadata |
| 44 | +
|
| 45 | + # defines column db type used to store payload. Available option: BYTEA (default), JSON, JSONB |
| 46 | + stored-as = BYTEA |
| 47 | + } |
| 48 | + } |
| 49 | +
|
| 50 | + snapshot-store { |
| 51 | + plugin = "akka.persistence.snapshot-store.postgresql" |
| 52 | + postgresql { |
| 53 | + # qualified type name of the PostgreSql persistence journal actor |
| 54 | + class = "Akka.Persistence.PostgreSql.Snapshot.PostgreSqlSnapshotStore, Akka.Persistence.PostgreSql" |
| 55 | +
|
| 56 | + # dispatcher used to drive journal actor |
| 57 | + plugin-dispatcher = ""akka.actor.default-dispatcher"" |
| 58 | +
|
| 59 | + # connection string used for database access |
| 60 | + connection-string = "" |
| 61 | +
|
| 62 | + # default SQL commands timeout |
| 63 | + connection-timeout = 30s |
| 64 | +
|
| 65 | + # PostgreSql schema name to table corresponding with persistent journal |
| 66 | + schema-name = public |
| 67 | +
|
| 68 | + # PostgreSql table corresponding with persistent journal |
| 69 | + table-name = snapshot_store |
| 70 | +
|
| 71 | + # should corresponding journal table be initialized automatically |
| 72 | + auto-initialize = off |
| 73 | + |
| 74 | + # defines column db type used to store payload. Available option: BYTEA (default), JSON, JSONB |
| 75 | + stored-as = BYTEA |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | +``` |
| 80 | +### Table Schema |
| 81 | + |
| 82 | +PostgreSql persistence plugin defines a default table schema used for journal, snapshot store and metadate table. |
| 83 | + |
| 84 | +```SQL |
| 85 | +CREATE TABLE {your_journal_table_name} ( |
| 86 | + ordering BIGSERIAL NOT NULL PRIMARY KEY, |
| 87 | + persistence_id VARCHAR(255) NOT NULL, |
| 88 | + sequence_nr BIGINT NOT NULL, |
| 89 | + is_deleted BOOLEAN NOT NULL, |
| 90 | + created_at BIGINT NOT NULL, |
| 91 | + manifest VARCHAR(500) NOT NULL, |
| 92 | + payload BYTEA NOT NULL, |
| 93 | + tags VARCHAR(100) NULL, |
| 94 | + CONSTRAINT {your_journal_table_name}_uq UNIQUE (persistence_id, sequence_nr) |
| 95 | +); |
| 96 | + |
| 97 | +CREATE TABLE {your_snapshot_table_name} ( |
| 98 | + persistence_id VARCHAR(255) NOT NULL, |
| 99 | + sequence_nr BIGINT NOT NULL, |
| 100 | + created_at BIGINT NOT NULL, |
| 101 | + manifest VARCHAR(500) NOT NULL, |
| 102 | + snapshot BYTEA NOT NULL, |
| 103 | + CONSTRAINT {your_snapshot_table_name}_pk PRIMARY KEY (persistence_id, sequence_nr) |
| 104 | +); |
| 105 | + |
| 106 | +CREATE TABLE {your_metadata_table_name} ( |
| 107 | + persistence_id VARCHAR(255) NOT NULL, |
| 108 | + sequence_nr BIGINT NOT NULL, |
| 109 | + CONSTRAINT {your_metadata_table_name}_pk PRIMARY KEY (persistence_id, sequence_nr) |
| 110 | +); |
| 111 | +``` |
58 | 112 |
|
59 | | -**created_at and created_at_ticks - The max precision of a PostgreSQL timestamp is 6. The max precision of a .Net DateTime object is 7. Because of this differences, the additional ticks are saved in a separate column and combined during deserialization. There is also a check constraint restricting created_at_ticks to the range [0,10) to ensure that there are no precision differences in the opposite direction.** |
| 113 | +### Migration |
60 | 114 |
|
61 | | -Underneath Akka.Persistence.PostgreSql uses the Npgsql library to communicate with the database. You may choose not to use a dedicated built in ones, but to create your own being better fit for your use case. To do so, you have to create your own versions of `IJournalQueryBuilder` and `IJournalQueryMapper` (for custom journals) or `ISnapshotQueryBuilder` and `ISnapshotQueryMapper` (for custom snapshot store) and then attach inside journal, just like in the example below: |
| 115 | +#### From 1.0.6 to 1.1.0 |
| 116 | +```SQL |
| 117 | +CREATE TABLE {your_metadata_table_name} ( |
| 118 | + persistence_id VARCHAR(255) NOT NULL, |
| 119 | + sequence_nr BIGINT NOT NULL, |
| 120 | + CONSTRAINT {your_metadata_table_name}_pk PRIMARY KEY (persistence_id, sequence_nr) |
| 121 | +); |
62 | 122 |
|
63 | | -```csharp |
64 | | -class MyCustomPostgreSqlJournal: Akka.Persistence.PostgreSql.Journal.PostgreSqlJournal |
65 | | -{ |
66 | | - public MyCustomPostgreSqlJournal() : base() |
67 | | - { |
68 | | - QueryBuilder = new MyCustomJournalQueryBuilder(); |
69 | | - QueryMapper = new MyCustomJournalQueryMapper(); |
70 | | - } |
71 | | -} |
72 | | -``` |
| 123 | +ALTER TABLE {your_journal_table_name} DROP CONSTRAINT {your_journal_table_name}_pk; |
| 124 | +ALTER TABLE {your_journal_table_name} ADD COLUMN ordering BIGSERIAL NOT NULL PRIMARY KEY; |
| 125 | +ALTER TABLE {your_journal_table_name} ADD COLUMN tags VARCHAR(100) NULL; |
| 126 | +ALTER TABLE {your_journal_table_name} ADD CONSTRAINT {your_journal_table_name}_uq UNIQUE (persistence_id, sequence_nr); |
| 127 | +ALTER TABLE {your_journal_table_name} ADD COLUMN created_at_temp BIGINT NOT NULL; |
73 | 128 |
|
74 | | -The final step is to setup your custom journal using akka config: |
| 129 | +UPDATE {your_journal_table_name} SET created_at_temp=extract(epoch from create_at); |
75 | 130 |
|
76 | | -``` |
77 | | -akka.persistence.journal.postgresql.class = "MyModule.MyCustomPostgreSqlJournal, MyModule" |
| 131 | +ALTER TABLE {your_journal_table_name} DROP COLUMN create_at; |
| 132 | +ALTER TABLE {your_journal_table_name} DROP COLUMN created_at_ticks; |
| 133 | +ALTER TABLE {your_journal_table_name} RENAME COLUMN created_at_temp TO create_at; |
78 | 134 | ``` |
79 | 135 |
|
80 | 136 | ### Tests |
|
0 commit comments