Skip to content
This repository was archived by the owner on Mar 20, 2025. It is now read-only.

Commit 023571b

Browse files
Merge pull request #27 from akkadotnet/dev
Release 1.1.1
2 parents 0ebab83 + d416a6e commit 023571b

38 files changed

+1353
-784
lines changed

.gitattributes

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@
2323
*.RTF diff=astextplain
2424

2525
# Needed for Mono build shell script
26-
*.sh -text eol=lf
26+
*.sh text eol=lf
27+
28+
# Needed for API Approvals
29+
*.txt text eol=crlf

README.md

Lines changed: 119 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,79 +2,135 @@
22

33
Akka Persistence journal and snapshot store backed by PostgreSql database.
44

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**.
246

257
### Configuration
268

279
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):
2810

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+
```
58112

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
60114

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+
);
62122

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;
73128

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);
75130

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;
78134
```
79135

80136
### Tests

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
#### 1.1.2 January 2017 ####
2+
Updated for Akka.NET 1.1.2.
3+
4+
#### 1.0.6 December 10 2015 ####
5+
16
#### 1.0.5 August 08 2015 ####
27

38
- Changed tables schema: renamed payload_type column to manifest for journal and snapshot tables

build.fsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ let testOutput = "TestResults"
5151
let nugetDir = binDir @@ "nuget"
5252
let workingDir = binDir @@ "build"
5353
let libDir = workingDir @@ @"lib\net45\"
54-
let nugetExe = FullName @"src\.nuget\NuGet.exe"
54+
let nugetExe = FullName @"./src/.nuget/NuGet.exe"
5555
let slnFile = "./src/Akka.Persistence.PostgreSql.sln"
5656

5757
open Fake.RestorePackageHelper
5858
Target "RestorePackages" (fun _ ->
59-
slnFile
60-
|> RestoreMSSolutionPackages (fun p ->
61-
{ p with
62-
OutputPath = "./src/packages"
63-
Retries = 4 })
64-
)
59+
slnFile
60+
|> RestoreMSSolutionPackages (fun p ->
61+
{ p with
62+
OutputPath = "./src/packages"
63+
Retries = 4 })
64+
)
6565

6666
//--------------------------------------------------------------------------------
6767
// Clean build results
@@ -123,7 +123,7 @@ Target "CleanTests" <| fun _ ->
123123
//--------------------------------------------------------------------------------
124124
// Run tests
125125

126-
open XUnit2Helper
126+
open Fake.Testing
127127
Target "RunTests" <| fun _ ->
128128
let xunitTestAssemblies = !! "src/**/bin/Release/*.Tests.dll"
129129

@@ -132,7 +132,8 @@ Target "RunTests" <| fun _ ->
132132
let xunitToolPath = findToolInSubPath "xunit.console.exe" "src/packages/xunit.runner.console*/tools"
133133
printfn "Using XUnit runner: %s" xunitToolPath
134134
xUnit2
135-
(fun p -> { p with OutputDir = testOutput; ToolPath = xunitToolPath })
135+
(fun p -> { p with HtmlOutputPath = Some(testOutput @@ "xunit.html")
136+
ForceTeamCity = true })
136137
xunitTestAssemblies
137138

138139
//--------------------------------------------------------------------------------
@@ -184,13 +185,15 @@ let updateNugetPackages _ =
184185
ConfigFile = Some (getConfigFile isPreRelease)
185186
Prerelease = true
186187
ToolPath = nugetExe
187-
RepositoryPath = "src/Packages"
188+
RepositoryPath = "./src/packages"
188189
Ids = getPackages project
189190
}) config
190191

191192
Target "UpdateDependencies" <| fun _ ->
192193
printfn "Invoking updateNugetPackages"
193-
updateNugetPackages()
194+
match Fake.EnvironmentHelper.isMono with
195+
| false -> updateNugetPackages()
196+
| true -> () // don't run this function if building with Mono, it doesn't work
194197

195198
//--------------------------------------------------------------------------------
196199
// Clean nuget directory

build.sh

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,20 @@
11
#!/bin/bash
2+
SCRIPT_PATH="${BASH_SOURCE[0]}";
3+
if ([ -h "${SCRIPT_PATH}" ]) then
4+
while([ -h "${SCRIPT_PATH}" ]) do SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
5+
fi
6+
pushd . > /dev/null
7+
cd `dirname ${SCRIPT_PATH}` > /dev/null
8+
SCRIPT_PATH=`pwd`;
9+
popd > /dev/null
10+
11+
if ! [ -f $SCRIPT_PATH/src/.nuget/nuget.exe ]
12+
then
13+
wget "https://www.nuget.org/nuget.exe" -P $SCRIPT_PATH/src/.nuget/
14+
fi
15+
16+
mono $SCRIPT_PATH/src/.nuget/nuget.exe update -self
17+
218

319
SCRIPT_PATH="${BASH_SOURCE[0]}";
420
if ([ -h "${SCRIPT_PATH}" ]) then
@@ -11,9 +27,13 @@ popd > /dev/null
1127

1228
mono $SCRIPT_PATH/src/.nuget/NuGet.exe update -self
1329

14-
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install FAKE -OutputDirectory $SCRIPT_PATH/src/packages -ExcludeVersion -Version 3.28.8
30+
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install FAKE -OutputDirectory $SCRIPT_PATH/src/packages -ExcludeVersion -Version 4.47.1
31+
32+
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install xunit.runner.console -OutputDirectory $SCRIPT_PATH/src/packages/FAKE -ExcludeVersion -Version 2.0.0
33+
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install NUnit.Console -OutputDirectory $SCRIPT_PATH/src/packages/FAKE -ExcludeVersion -Version 3.2.1
1534

16-
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install xunit.runners -OutputDirectory $SCRIPT_PATH/src/packages/FAKE -ExcludeVersion -Version 2.0.0
35+
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install NBench.Runner -OutputDirectory $SCRIPT_PATH/src/packages -ExcludeVersion -Version 0.3.3
36+
1737

1838
if ! [ -e $SCRIPT_PATH/src/packages/SourceLink.Fake/tools/SourceLink.fsx ] ; then
1939
mono $SCRIPT_PATH/src/.nuget/NuGet.exe install SourceLink.Fake -OutputDirectory $SCRIPT_PATH/src/packages -ExcludeVersion

src/.nuget/NuGet.Config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
<solution>
44
<add key="disableSourceControlIntegration" value="true" />
55
</solution>
6+
<packageSources>
7+
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
8+
</packageSources>
69
</configuration>

src/.nuget/NuGet.exe

2.17 MB
Binary file not shown.

0 commit comments

Comments
 (0)