├── .editorconfig
├── .gitattributes
├── .github
└── dependabot.yml
├── .gitignore
├── Akka.Persistence.MongoDb.sln
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE.md
├── NuGet.Config
├── README.md
├── RELEASE_NOTES.md
├── appveyor.yml
├── build-system
├── README.md
├── azure-pipeline.template.yaml
├── linux-pr-validation.yaml
├── windows-pr-validation.yaml
└── windows-release.yaml
├── build.ps1
├── coverlet.runsettings
├── docs
├── api
│ └── index.md
├── articles
│ ├── index.md
│ └── toc.yml
├── docfx.json
├── images
│ └── icon.png
├── index.md
└── toc.yml
├── global.json
├── scripts
├── bumpVersion.ps1
└── getReleaseNotes.ps1
├── serve-docs.cmd
├── serve-docs.ps1
└── src
├── Akka.Persistence.MongoDb.Hosting
├── Akka.Persistence.MongoDb.Hosting.csproj
├── AkkaPersistenceMongoDbHostingExtensions.cs
├── MongoDbGridFsSnapshotOptions.cs
├── MongoDbJournalOptions.cs
├── MongoDbSnapshotOptions.cs
└── README.md
├── Akka.Persistence.MongoDb.Tests
├── Akka.Persistence.MongoDb.Tests.csproj
├── Bug25FixSpec.cs
├── Bug61FixSpec.cs
├── DatabaseFixture.cs
├── GridFS
│ ├── MongoDbGridFsLegacySerializationSnapshotStoreSpec.cs
│ ├── MongoDbGridFsSnapshotStoreSaveSnapshotSpec.cs
│ ├── MongoDbGridFsSnapshotStoreSpec.cs
│ └── Serialization
│ │ └── MongoDbGridFsSnapshotStoreSerializationSpec.cs
├── Hosting
│ ├── MongoDbJournalOptionsSpec.cs
│ └── MongoDbSnapshotOptionsSpec.cs
├── JournalTestActor.cs
├── MongoDbAllEventsSpec.cs
├── MongoDbCurrentAllEventsSpec.cs
├── MongoDbCurrentEventsByPersistenceIdsSpec.cs
├── MongoDbCurrentEventsByTagSpec.cs
├── MongoDbCurrentPersistenceIdsSpec.cs
├── MongoDbEventsByPersistenceIdSpec.cs
├── MongoDbEventsByTagSpec.cs
├── MongoDbJournalPerfSpec.cs
├── MongoDbJournalSetupSpec.cs
├── MongoDbJournalSpec.cs
├── MongoDbLegacySerializationJournalSpec.cs
├── MongoDbLegacySerializationSnapshotStoreSpec.cs
├── MongoDbPersistenceIdsSpec.cs
├── MongoDbSettingsSpec.cs
├── MongoDbSnapshotStoreSaveSnapshotSpec.cs
├── MongoDbSnapshotStoreSetupSpec.cs
├── MongoDbSnapshotStoreSpec.cs
└── Serialization
│ ├── MongoDbJournalSerializationSpec.cs
│ └── MongoDbSnapshotStoreSerializationSpec.cs
├── Akka.Persistence.MongoDb
├── Akka.Persistence.MongoDb.csproj
├── FullTypeNameDiscriminatorConvention.cs
├── FullTypeNameObjectSerializer.cs
├── Journal
│ ├── JournalEntry.cs
│ ├── MetadataEntry.cs
│ ├── MongoDbJournal.cs
│ └── MongoDbJournalQueries.cs
├── MongoDbPersistence.cs
├── MongoDbPersistenceProvider.cs
├── MongoDbPersistenceSetup.cs
├── MongoDbSettings.cs
├── Query
│ ├── AllEventsPublisher.cs
│ ├── AllPersistenceIdsPublisher.cs
│ ├── DeliveryBuffer.cs
│ ├── EventByPersistenceIdPublisher.cs
│ ├── EventsByTagPublisher.cs
│ ├── MongoDbReadJournal.cs
│ ├── MongoDbReadJournalProvider.cs
│ ├── QueryApi.cs
│ └── SubscriptionDroppedException.cs
├── Snapshot
│ ├── GridFsPayloadEnvelope.cs
│ ├── MongoDbGridFSSnapshotStore.cs
│ ├── MongoDbSnapshotStore.cs
│ └── SnapshotEntry.cs
└── reference.conf
└── examples
├── LargeSnapshot
├── Actors
│ └── PersistentActor.cs
├── LargeSnapshot.csproj
├── Program.cs
└── appsettings.json
└── start_docker.ps1
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; This file is for unifying the coding style for different editors and IDEs.
2 | ; More information at http://EditorConfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = CRLF
8 |
9 | [*.cs]
10 | indent_style = space
11 | indent_size = 4
12 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 |
5 | # Custom for Visual Studio
6 | *.cs diff=csharp
7 | *.sln merge=union
8 | *.csproj merge=union
9 | *.vbproj merge=union
10 | *.fsproj merge=union
11 | *.dbproj merge=union
12 |
13 | # Standard to msysgit
14 | *.doc diff=astextplain
15 | *.DOC diff=astextplain
16 | *.docx diff=astextplain
17 | *.DOCX diff=astextplain
18 | *.dot diff=astextplain
19 | *.DOT diff=astextplain
20 | *.pdf diff=astextplain
21 | *.PDF diff=astextplain
22 | *.rtf diff=astextplain
23 | *.RTF diff=astextplain
24 |
25 | # Needed for Mono build shell script
26 | *.sh -text eol=lf
27 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: nuget
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | target-branch: dev
9 | ignore:
10 | - dependency-name: Mongo2Go
11 | versions:
12 | - 3.0.0
13 | - 3.1.0
14 | - 3.1.1
15 | - dependency-name: Akka.Persistence.TCK
16 | versions:
17 | - 1.4.17
18 | - dependency-name: akka.streams
19 | versions:
20 | - 1.4.17
21 | - dependency-name: Akka.Persistence.Query
22 | versions:
23 | - 1.4.17
24 | - dependency-name: FluentAssertions
25 | versions:
26 | - 5.10.3
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Custom
2 | tools/
3 | build/
4 | .nuget/
5 | .dotnet/
6 | .idea/
7 | .[Dd][Ss]_[Ss]tore
8 |
9 | ## Ignore Visual Studio temporary files, build results, and
10 | ## files generated by popular Visual Studio add-ons.
11 | ##
12 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
13 |
14 | # User-specific files
15 | *.suo
16 | *.user
17 | *.userosscache
18 | *.sln.docstates
19 |
20 | # User-specific files (MonoDevelop/Xamarin Studio)
21 | *.userprefs
22 |
23 | # Build results
24 | [Dd]ebug/
25 | [Dd]ebugPublic/
26 | [Rr]elease/
27 | [Rr]eleases/
28 | x64/
29 | x86/
30 | bld/
31 | [Bb]in/
32 | [Oo]bj/
33 | [Ll]og/
34 |
35 | #FAKE
36 | .fake
37 | tools/
38 |
39 | #DocFx output
40 | _site/
41 |
42 | # Visual Studio 2015 cache/options directory
43 | .vs/
44 | # Uncomment if you have tasks that create the project's static files in wwwroot
45 | #wwwroot/
46 |
47 | # MSTest test Results
48 | [Tt]est[Rr]esult*/
49 | [Bb]uild[Ll]og.*
50 |
51 | # NUNIT
52 | *.VisualState.xml
53 | TestResult.xml
54 |
55 | # Build Results of an ATL Project
56 | [Dd]ebugPS/
57 | [Rr]eleasePS/
58 | dlldata.c
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 | **/Properties/launchSettings.json
65 |
66 | *_i.c
67 | *_p.c
68 | *_i.h
69 | *.ilk
70 | *.meta
71 | *.obj
72 | *.pch
73 | *.pdb
74 | *.pgc
75 | *.pgd
76 | *.rsp
77 | *.sbr
78 | *.tlb
79 | *.tli
80 | *.tlh
81 | *.tmp
82 | *.tmp_proj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # Visual Studio code coverage results
132 | *.coverage
133 | *.coveragexml
134 |
135 | # NCrunch
136 | _NCrunch_*
137 | .*crunch*.local.xml
138 | nCrunchTemp_*
139 |
140 | # MightyMoose
141 | *.mm.*
142 | AutoTest.Net/
143 |
144 | # Web workbench (sass)
145 | .sass-cache/
146 |
147 | # Installshield output folder
148 | [Ee]xpress/
149 |
150 | # DocProject is a documentation generator add-in
151 | DocProject/buildhelp/
152 | DocProject/Help/*.HxT
153 | DocProject/Help/*.HxC
154 | DocProject/Help/*.hhc
155 | DocProject/Help/*.hhk
156 | DocProject/Help/*.hhp
157 | DocProject/Help/Html2
158 | DocProject/Help/html
159 |
160 | # Click-Once directory
161 | publish/
162 |
163 | # Publish Web Output
164 | *.[Pp]ublish.xml
165 | *.azurePubxml
166 | # TODO: Comment the next line if you want to checkin your web deploy settings
167 | # but database connection strings (with potential passwords) will be unencrypted
168 | *.pubxml
169 | *.publishproj
170 |
171 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
172 | # checkin your Azure Web App publish settings, but sensitive information contained
173 | # in these scripts will be unencrypted
174 | PublishScripts/
175 |
176 | # NuGet Packages
177 | *.nupkg
178 | # The packages folder can be ignored because of Package Restore
179 | **/packages/*
180 | # except build/, which is used as an MSBuild target.
181 | !**/packages/build/
182 | # Uncomment if necessary however generally it will be regenerated when needed
183 | #!**/packages/repositories.config
184 | # NuGet v3's project.json files produces more ignorable files
185 | *.nuget.props
186 | *.nuget.targets
187 |
188 | # Microsoft Azure Build Output
189 | csx/
190 | *.build.csdef
191 |
192 | # Microsoft Azure Emulator
193 | ecf/
194 | rcf/
195 |
196 | # Windows Store app package directories and files
197 | AppPackages/
198 | BundleArtifacts/
199 | Package.StoreAssociation.xml
200 | _pkginfo.txt
201 |
202 | # Visual Studio cache files
203 | # files ending in .cache can be ignored
204 | *.[Cc]ache
205 | # but keep track of directories ending in .cache
206 | !*.[Cc]ache/
207 |
208 | # Others
209 | ClientBin/
210 | ~$*
211 | *~
212 | *.dbmdl
213 | *.dbproj.schemaview
214 | *.jfm
215 | *.pfx
216 | *.publishsettings
217 | orleans.codegen.cs
218 |
219 | # Since there are multiple workflows, uncomment next line to ignore bower_components
220 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
221 | #bower_components/
222 |
223 | # RIA/Silverlight projects
224 | Generated_Code/
225 |
226 | # Backup & report files from converting an old project file
227 | # to a newer Visual Studio version. Backup files are not needed,
228 | # because we have git ;-)
229 | _UpgradeReport_Files/
230 | Backup*/
231 | UpgradeLog*.XML
232 | UpgradeLog*.htm
233 |
234 | # SQL Server files
235 | *.mdf
236 | *.ldf
237 | *.ndf
238 |
239 | # Business Intelligence projects
240 | *.rdl.data
241 | *.bim.layout
242 | *.bim_*.settings
243 |
244 | # Microsoft Fakes
245 | FakesAssemblies/
246 |
247 | # GhostDoc plugin setting file
248 | *.GhostDoc.xml
249 |
250 | # Node.js Tools for Visual Studio
251 | .ntvs_analysis.dat
252 | node_modules/
253 |
254 | # Typescript v1 declaration files
255 | typings/
256 |
257 | # Visual Studio 6 build log
258 | *.plg
259 |
260 | # Visual Studio 6 workspace options file
261 | *.opt
262 |
263 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
264 | *.vbw
265 |
266 | # Visual Studio LightSwitch build output
267 | **/*.HTMLClient/GeneratedArtifacts
268 | **/*.DesktopClient/GeneratedArtifacts
269 | **/*.DesktopClient/ModelManifest.xml
270 | **/*.Server/GeneratedArtifacts
271 | **/*.Server/ModelManifest.xml
272 | _Pvt_Extensions
273 |
274 | # Paket dependency manager
275 | .paket/paket.exe
276 | paket-files/
277 |
278 | # FAKE - F# Make
279 | .fake/
280 |
281 | # JetBrains Rider
282 | .idea/
283 | *.sln.iml
284 |
285 | # CodeRush
286 | .cr/
287 |
288 | # Python Tools for Visual Studio (PTVS)
289 | __pycache__/
290 | *.pyc
291 |
292 | # Cake - Uncomment if you are using it
293 | # tools/**
294 | # !tools/packages.config
295 |
296 | # Telerik's JustMock configuration file
297 | *.jmconfig
298 |
299 | # BizTalk build output
300 | *.btp.cs
301 | *.btm.cs
302 | *.odx.cs
303 | *.xsd.cs
304 |
--------------------------------------------------------------------------------
/Akka.Persistence.MongoDb.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33723.286
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.MongoDb", "src\Akka.Persistence.MongoDb\Akka.Persistence.MongoDb.csproj", "{E945AABA-2779-41E8-9B43-8898FFD64F22}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.MongoDb.Tests", "src\Akka.Persistence.MongoDb.Tests\Akka.Persistence.MongoDb.Tests.csproj", "{0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{BE1178E1-2069-4762-82E5-43805913DCB8}"
11 | ProjectSection(SolutionItems) = preProject
12 | build.ps1 = build.ps1
13 | README.md = README.md
14 | RELEASE_NOTES.md = RELEASE_NOTES.md
15 | Directory.Build.props = Directory.Build.props
16 | Directory.Packages.props = Directory.Packages.props
17 | EndProjectSection
18 | EndProject
19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.MongoDb.Hosting", "src\Akka.Persistence.MongoDb.Hosting\Akka.Persistence.MongoDb.Hosting.csproj", "{72B8C165-FE00-465F-A2E9-60B4B79F81AF}"
20 | EndProject
21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{57AE52C4-1E22-42B9-9214-4FEE4F5DFC70}"
22 | EndProject
23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LargeSnapshot", "src\examples\LargeSnapshot\LargeSnapshot.csproj", "{8FA9D3B5-C67E-49A6-8449-74E060458F25}"
24 | EndProject
25 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-system", "build-system", "{A57DA6C5-C818-499C-B0AF-F22490AE2015}"
26 | ProjectSection(SolutionItems) = preProject
27 | build-system\azure-pipeline.template.yaml = build-system\azure-pipeline.template.yaml
28 | build-system\linux-pr-validation.yaml = build-system\linux-pr-validation.yaml
29 | build-system\windows-pr-validation.yaml = build-system\windows-pr-validation.yaml
30 | build-system\windows-release.yaml = build-system\windows-release.yaml
31 | EndProjectSection
32 | EndProject
33 | Global
34 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
35 | Debug|Any CPU = Debug|Any CPU
36 | Release|Any CPU = Release|Any CPU
37 | EndGlobalSection
38 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
39 | {E945AABA-2779-41E8-9B43-8898FFD64F22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40 | {E945AABA-2779-41E8-9B43-8898FFD64F22}.Debug|Any CPU.Build.0 = Debug|Any CPU
41 | {E945AABA-2779-41E8-9B43-8898FFD64F22}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 | {E945AABA-2779-41E8-9B43-8898FFD64F22}.Release|Any CPU.Build.0 = Release|Any CPU
43 | {0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44 | {0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Debug|Any CPU.Build.0 = Debug|Any CPU
45 | {0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {0F9B9BC6-9F86-40E8-BA9B-D27BF3AC7970}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
48 | {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
49 | {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {72B8C165-FE00-465F-A2E9-60B4B79F81AF}.Release|Any CPU.Build.0 = Release|Any CPU
51 | {8FA9D3B5-C67E-49A6-8449-74E060458F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52 | {8FA9D3B5-C67E-49A6-8449-74E060458F25}.Debug|Any CPU.Build.0 = Debug|Any CPU
53 | {8FA9D3B5-C67E-49A6-8449-74E060458F25}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 | {8FA9D3B5-C67E-49A6-8449-74E060458F25}.Release|Any CPU.Build.0 = Release|Any CPU
55 | EndGlobalSection
56 | GlobalSection(SolutionProperties) = preSolution
57 | HideSolutionNode = FALSE
58 | EndGlobalSection
59 | GlobalSection(ExtensibilityGlobals) = postSolution
60 | SolutionGuid = {D4F3F966-EB9C-443A-8158-419703A68B92}
61 | EndGlobalSection
62 | GlobalSection(NestedProjects) = preSolution
63 | {8FA9D3B5-C67E-49A6-8449-74E060458F25} = {57AE52C4-1E22-42B9-9214-4FEE4F5DFC70}
64 | {A57DA6C5-C818-499C-B0AF-F22490AE2015} = {BE1178E1-2069-4762-82E5-43805913DCB8}
65 | EndGlobalSection
66 | EndGlobal
67 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copyright © 2013-2021 Akka.NET Project
4 | Akka.NET Contrib
5 | 1.5.41-beta1
6 | http://getakka.net/images/akkalogo.png
7 | https://github.com/akkadotnet/Akka.Persistence.MongoDB
8 | https://github.com/akkadotnet/Akka.Persistence.MongoDB/blob/master/LICENSE.md
9 | * [Optimize write transaction usage](https://github.com/akkadotnet/Akka.Persistence.MongoDB/pull/410)
10 |
11 | We're optimizing how write transaction is being done. Following the [MongoDb documentation](https://www.mongodb.com/docs/manual/core/write-operations-atomicity/#atomicity-and-transactions) that all single document writes are atomic, we're not using transaction for single document writes anymore.
12 | true
13 | Akka Persistence journal and snapshot store backed by MongoDB database.
14 | $(NoWarn);CS1591
15 | latest
16 |
17 |
18 | net7.0
19 | net472
20 | netstandard2.1
21 | net472
22 |
23 |
24 |
25 |
26 |
27 |
28 | true
29 |
30 | true
31 |
32 | true
33 | snupkg
34 |
35 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | 3.0.0
5 | 1.5.42
6 | 1.5.42
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | all
22 | runtime; build; native; contentfiles; analyzers; buildtransitive
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: '1.0.{build}'
2 | skip_tags: true
3 | image: Visual Studio 2015
4 | configuration: Release
5 | build_script:
6 | - cmd: build.cmd Build
7 | test_script:
8 | - cmd: build.cmd RunTests
9 |
--------------------------------------------------------------------------------
/build-system/README.md:
--------------------------------------------------------------------------------
1 | # Azure Pipelines Build Files
2 | These `.yaml` files are used by Windows Azure DevOps Pipelines to help execute the following types of builds:
3 |
4 | - Pull request validation on Linux (Mono / .NET Core)
5 | - Pull request validation on Windows (.NET Framework / .NET Core)
6 | - NuGet releases with automatic release notes posted to a Github Release repository.
7 |
8 | **NOTE**: you will need to change some of the pipeline variables inside the `windows-release.yaml` for your specific project and you will also want to create variable groups with your signing and NuGet push information.
--------------------------------------------------------------------------------
/build-system/azure-pipeline.template.yaml:
--------------------------------------------------------------------------------
1 | parameters:
2 | name: ''
3 | displayName: ''
4 | vmImage: ''
5 | outputDirectory: ''
6 | artifactName: ''
7 | timeoutInMinutes: 120
8 |
9 | jobs:
10 | - job: ${{ parameters.name }}
11 | displayName: ${{ parameters.displayName }}
12 | timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
13 |
14 | pool:
15 | vmImage: ${{ parameters.vmImage }}
16 |
17 | steps:
18 | - checkout: self # self represents the repo where the initial Pipelines YAML file was found
19 | clean: false # whether to fetch clean each time
20 | submodules: recursive # set to 'true' for a single level of submodules or 'recursive' to get submodules of submodules
21 | persistCredentials: true
22 |
23 | - task: UseDotNet@2
24 | displayName: 'Use .NET'
25 | inputs:
26 | packageType: 'sdk'
27 | useGlobalJson: true
28 |
29 | - task: UseDotNet@2
30 | displayName: "Use .NET 7 runtime"
31 | inputs:
32 | packageType: runtime
33 | version: 7.x
34 |
35 | # install libcrypto.so.1.1 in linux,this is missing in the latest ubuntu release
36 | # see https://github.com/Mongo2Go/Mongo2Go/?tab=readme-ov-file#mongo2go-410-january-30-2025
37 | - script: |
38 | echo "deb http://security.ubuntu.com/ubuntu focal-security main" | sudo tee /etc/apt/sources.list.d/focal-security.list
39 | sudo apt update
40 | sudo apt install -y libssl1.1
41 | displayName: 'Install libcrypto.so.1.1'
42 | condition: eq( variables['Agent.OS'], 'Linux' )
43 |
44 | - script: dotnet tool restore
45 | displayName: 'Restore dotnet tools'
46 |
47 | - pwsh: |
48 | .\build.ps1
49 | displayName: 'Update Release Notes'
50 | continueOnError: false
51 |
52 | - script: dotnet build -c Release
53 | displayName: 'dotnet build'
54 | continueOnError: false
55 |
56 | - powershell: |
57 | Get-ChildItem `
58 | -Recurse `
59 | -File `
60 | -Include '*.Tests.csproj' `
61 | -Exclude '*examples*', '*benchmarks*' `
62 | | ForEach-Object { `
63 | dotnet test -c Release --no-build --logger:trx --collect:"XPlat Code Coverage" --results-directory TestResults --settings coverlet.runsettings $_.FullName `
64 | }
65 | displayName: 'Run tests'
66 | env:
67 | TEST_ENVIRONMENT: CICD
68 | continueOnError: true # Allow continuation even if tests fail
69 |
70 | - task: PublishTestResults@2
71 | inputs:
72 | testResultsFormat: VSTest
73 | testResultsFiles: '**/*.trx' #TestResults folder usually
74 | testRunTitle: ${{ parameters.name }}
75 | mergeTestResults: true
76 | failTaskOnFailedTests: false
77 | publishRunAttachments: true
78 |
79 | - pwsh: |
80 | $coverageFiles = Get-ChildItem -Path "$(Build.SourcesDirectory)/TestResults" -Filter "*.cobertura.xml" -Recurse
81 | $hasCoverageFiles = $coverageFiles.Count -gt 0
82 | Write-Host "##vso[task.setvariable variable=HasCoverageFiles]$hasCoverageFiles"
83 | displayName: 'Check for Coverage Files'
84 | condition: always()
85 | continueOnError: true
86 |
87 | - task: reportgenerator@5
88 | displayName: ReportGenerator
89 | # Only run if coverage files exist
90 | condition: and(always(), eq(variables['HasCoverageFiles'], 'True'))
91 | continueOnError: true
92 | inputs:
93 | reports: '$(Build.SourcesDirectory)/TestResults/**/*.cobertura.xml'
94 | targetdir: '$(Build.SourcesDirectory)/coveragereport'
95 | reporttypes: 'HtmlInline_AzurePipelines;Cobertura;Badges'
96 | assemblyfilters: '-xunit*'
97 | publishCodeCoverageResults: true
98 |
99 | - publish: $(Build.SourcesDirectory)/coveragereport
100 | displayName: 'Publish Coverage Report'
101 | # Only run if coverage files exist
102 | condition: and(always(), eq(variables['HasCoverageFiles'], 'True'))
103 | continueOnError: true
104 | artifact: 'CoverageReports-$(Agent.OS)-$(Build.BuildId)'
105 |
106 | - script: dotnet pack -c Release --no-build -o $(Build.ArtifactStagingDirectory)/nuget
107 | displayName: 'Create packages'
108 |
109 | - task: PublishBuildArtifacts@1
110 | displayName: 'Publish artifacts'
111 | inputs:
112 | PathtoPublish: '$(Build.ArtifactStagingDirectory)/nuget'
113 | ArtifactName: 'nuget'
114 | publishLocation: 'Container'
115 |
116 | - script: 'echo 1>&2'
117 | failOnStderr: true
118 | displayName: 'If above is partially succeeded, then fail'
119 | condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues')
120 |
--------------------------------------------------------------------------------
/build-system/linux-pr-validation.yaml:
--------------------------------------------------------------------------------
1 | # Pull request validation for Linux against the `dev` and `master` branches
2 | # See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference
3 | trigger:
4 | branches:
5 | include:
6 | - dev
7 | - master
8 |
9 | name: $(SourceBranchName)_$(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)
10 |
11 | pr:
12 | autoCancel: true # indicates whether additional pushes to a PR should cancel in-progress runs for the same PR. Defaults to true
13 | branches:
14 | include: [ dev, master ] # branch names which will trigger a build
15 |
16 | jobs:
17 | - template: azure-pipeline.template.yaml
18 | parameters:
19 | name: 'net_7_tests_linux'
20 | displayName: '.NET 7 Unit Tests (Linux)'
21 | vmImage: 'ubuntu-latest'
22 | outputDirectory: 'bin/nuget'
23 | artifactName: 'nuget_pack-$(Build.BuildId)'
24 |
--------------------------------------------------------------------------------
/build-system/windows-pr-validation.yaml:
--------------------------------------------------------------------------------
1 | # Pull request validation for Windows against the `dev` and `master` branches
2 | # See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference
3 | trigger:
4 | branches:
5 | include:
6 | - dev
7 | - master
8 |
9 | pr:
10 | autoCancel: true # indicates whether additional pushes to a PR should cancel in-progress runs for the same PR. Defaults to true
11 | branches:
12 | include: [ dev, master ] # branch names which will trigger a build
13 |
14 | name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)
15 |
16 | jobs:
17 | - template: azure-pipeline.template.yaml
18 | parameters:
19 | name: 'net_7_tests_windows'
20 | displayName: '.NET 7 Unit Tests (Windows)'
21 | vmImage: 'windows-latest'
22 | outputDirectory: 'bin/nuget'
23 | artifactName: 'nuget_pack-$(Build.BuildId)'
24 |
--------------------------------------------------------------------------------
/build-system/windows-release.yaml:
--------------------------------------------------------------------------------
1 | # Release task for Akka.Persistence.MongoDb
2 | # See https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema for reference
3 |
4 | trigger:
5 | branches:
6 | include:
7 | - refs/tags/*
8 | pr: none
9 |
10 | variables:
11 | - group: signingSecrets #create this group with SECRET variables `signingUsername` and `signingPassword`
12 | - group: nugetKeys
13 | - name: githubConnectionName
14 | value: AkkaDotNet_Releases
15 | - name: projectName
16 | value: Akka.Persistence.MongoDB
17 | - name: githubRepositoryName
18 | value: akkadotnet/Akka.Persistence.MongoDB
19 |
20 | jobs:
21 | - job: Release
22 | pool:
23 | vmImage: windows-latest
24 | demands: Cmd
25 | steps:
26 | - task: UseDotNet@2
27 | displayName: 'Use .NET SDK from global.json'
28 | inputs:
29 | useGlobalJson: true
30 |
31 | - powershell: ./build.ps1
32 | displayName: 'Update Release Notes'
33 |
34 | # Pack without version suffix for release
35 | - script: dotnet pack -c Release -o $(Build.ArtifactStagingDirectory)/nuget
36 | displayName: 'Create packages'
37 |
38 | - script: dotnet nuget push "$(Build.ArtifactStagingDirectory)\nuget\*.nupkg" --api-key $(nugetKey) --source https://api.nuget.org/v3/index.json --skip-duplicate
39 | displayName: 'Publish to NuGet.org'
40 |
41 | - task: GitHubRelease@0
42 | displayName: 'GitHub release (create)'
43 | inputs:
44 | gitHubConnection: $(githubConnectionName)
45 | repositoryName: $(githubRepositoryName)
46 | title: '$(projectName) v$(Build.SourceBranchName)'
47 | releaseNotesFile: 'RELEASE_NOTES.md'
48 | assets: '$(Build.ArtifactStagingDirectory)/nuget/*.nupkg'
49 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | . "$PSScriptRoot\scripts\getReleaseNotes.ps1"
2 | . "$PSScriptRoot\scripts\bumpVersion.ps1"
3 |
4 | ######################################################################
5 | # Step 1: Grab release notes and update solution metadata
6 | ######################################################################
7 | $releaseNotes = Get-ReleaseNotes -MarkdownFile (Join-Path -Path $PSScriptRoot -ChildPath "RELEASE_NOTES.md")
8 |
9 | # inject release notes into Directory.Buil
10 | UpdateVersionAndReleaseNotes -ReleaseNotesResult $releaseNotes -XmlFilePath (Join-Path -Path $PSScriptRoot -ChildPath "Directory.Build.props")
11 |
12 | Write-Output "Added release notes $releaseNotes"
--------------------------------------------------------------------------------
/coverlet.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | json,cobertura,lcov,teamcity,opencover
8 | [*.Tests]*,[*.Benchmark]*
9 |
10 | Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute
11 |
12 |
13 | **/examples/**/*
14 |
15 |
16 | false
17 |
18 |
19 | false
20 | true
21 | true
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/api/index.md:
--------------------------------------------------------------------------------
1 | # API Docs
--------------------------------------------------------------------------------
/docs/articles/index.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | Article text goes here.
--------------------------------------------------------------------------------
/docs/articles/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Introduction
2 | href: index.md
--------------------------------------------------------------------------------
/docs/docfx.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": [
3 | {
4 | "src": [
5 | {
6 | "files": [ "**/*.csproj" ],
7 | "exclude": [
8 | "**/obj/**",
9 | "**/bin/**",
10 | "_site/**",
11 | "**/*Tests*.csproj",
12 | "**/*Tests.*.csproj"
13 | ],
14 | "src": "../src"
15 | }
16 | ],
17 | "dest": "api"
18 | }
19 | ],
20 | "build": {
21 | "content": [
22 | {
23 | "files": [
24 | "api/**.yml",
25 | "api/index.md"
26 | ]
27 | },
28 | {
29 | "files": [
30 | "articles/**.md",
31 | "articles/**/toc.yml",
32 | "toc.yml",
33 | "*.md"
34 | ],
35 | "exclude": [
36 | "obj/**",
37 | "_site/**"
38 | ]
39 | },
40 | ],
41 | "resource": [
42 | {
43 | "files": [
44 | "images/**"
45 | ],
46 | "exclude": [
47 | "obj/**",
48 | "_site/**"
49 | ]
50 | }
51 | ],
52 | "dest": "_site",
53 | "globalMetadata": {
54 | "_appTitle": "Akka.Persistence.MongoDb",
55 | "_disableContribution": "true",
56 | "_appLogoPath": "/images/icon.png",
57 | },
58 | "globalMetadataFiles": [],
59 | "fileMetadataFiles": [],
60 | "template": [
61 | "default",
62 | "template"
63 | ],
64 | "postProcessors": [],
65 | "noLangKeyword": false
66 | }
67 | }
--------------------------------------------------------------------------------
/docs/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akkadotnet/Akka.Persistence.MongoDB/03d5db4b57f9e18373e99c7b48565d404fc9c5d1/docs/images/icon.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Introduction to My Project
--------------------------------------------------------------------------------
/docs/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Home
2 | href: index.md
3 | - name: Documentation
4 | href: articles/
5 | - name: API Reference
6 | href: api/
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.203",
4 | "rollForward": "major"
5 | }
6 | }
--------------------------------------------------------------------------------
/scripts/bumpVersion.ps1:
--------------------------------------------------------------------------------
1 | function UpdateVersionAndReleaseNotes {
2 | param (
3 | [Parameter(Mandatory=$true)]
4 | [PSCustomObject]$ReleaseNotesResult,
5 |
6 | [Parameter(Mandatory=$true)]
7 | [string]$XmlFilePath
8 | )
9 |
10 | # Load XML
11 | $xmlContent = New-Object XML
12 | $xmlContent.Load($XmlFilePath)
13 |
14 | # Update VersionPrefix and PackageReleaseNotes
15 | $versionPrefixElement = $xmlContent.SelectSingleNode("//VersionPrefix")
16 | $versionPrefixElement.InnerText = $ReleaseNotesResult.Version
17 |
18 | $packageReleaseNotesElement = $xmlContent.SelectSingleNode("//PackageReleaseNotes")
19 | $packageReleaseNotesElement.InnerText = $ReleaseNotesResult.ReleaseNotes
20 |
21 | # Save the updated XML
22 | $xmlContent.Save($XmlFilePath)
23 | }
24 |
25 | # Usage example:
26 | # $notes = Get-ReleaseNotes -MarkdownFile "$PSScriptRoot\RELEASE_NOTES.md"
27 | # $propsPath = Join-Path -Path (Get-Item $PSScriptRoot).Parent.FullName -ChildPath "Directory.Build.props"
28 | # UpdateVersionAndReleaseNotes -ReleaseNotesResult $notes -XmlFilePath $propsPath
29 |
--------------------------------------------------------------------------------
/scripts/getReleaseNotes.ps1:
--------------------------------------------------------------------------------
1 | function Get-ReleaseNotes {
2 | param (
3 | [Parameter(Mandatory=$true)]
4 | [string]$MarkdownFile
5 | )
6 |
7 | # Read markdown file content
8 | $content = Get-Content -Path $MarkdownFile -Raw
9 |
10 | # Split content based on headers
11 | $sections = $content -split "####"
12 |
13 | # Output object to store result
14 | $outputObject = [PSCustomObject]@{
15 | Version = $null
16 | Date = $null
17 | ReleaseNotes = $null
18 | }
19 |
20 | # Check if we have at least 3 sections (1. Before the header, 2. Header, 3. Release notes)
21 | if ($sections.Count -ge 3) {
22 | $header = $sections[1].Trim()
23 | $releaseNotes = $sections[2].Trim()
24 |
25 | # Extract version and date from the header
26 | $headerParts = $header -split " ", 2
27 | if ($headerParts.Count -eq 2) {
28 | $outputObject.Version = $headerParts[0]
29 | $outputObject.Date = $headerParts[1]
30 | }
31 |
32 | $outputObject.ReleaseNotes = $releaseNotes
33 | }
34 |
35 | # Return the output object
36 | return $outputObject
37 | }
38 |
39 | # Call function example:
40 | #$result = Get-ReleaseNotes -MarkdownFile "$PSScriptRoot\RELEASE_NOTES.md"
41 | #Write-Output "Version: $($result.Version)"
42 | #Write-Output "Date: $($result.Date)"
43 | #Write-Output "Release Notes:"
44 | #Write-Output $result.ReleaseNotes
45 |
--------------------------------------------------------------------------------
/serve-docs.cmd:
--------------------------------------------------------------------------------
1 | PowerShell.exe -file "serve-docs.ps1" %* .\docs\docfx.json --serve -p 8090
--------------------------------------------------------------------------------
/serve-docs.ps1:
--------------------------------------------------------------------------------
1 | # docfx.ps1
2 | $VisualStudioVersion = "15.0";
3 | $DotnetSDKVersion = "2.0.0";
4 |
5 | # Get dotnet paths
6 | $MSBuildExtensionsPath = "C:\Program Files\dotnet\sdk\" + $DotnetSDKVersion;
7 | $MSBuildSDKsPath = $MSBuildExtensionsPath + "\SDKs";
8 |
9 | # Get Visual Studio install path
10 | $VSINSTALLDIR = $(Get-ItemProperty "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7").$VisualStudioVersion;
11 |
12 | # Add Visual Studio environment variables
13 | $env:VisualStudioVersion = $VisualStudioVersion;
14 | $env:VSINSTALLDIR = $VSINSTALLDIR;
15 |
16 | # Add dotnet environment variables
17 | $env:MSBuildExtensionsPath = $MSBuildExtensionsPath;
18 | $env:MSBuildSDKsPath = $MSBuildSDKsPath;
19 |
20 | # Build our docs
21 | & .\tools\docfx.console\tools\docfx @args
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Hosting/Akka.Persistence.MongoDb.Hosting.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(NetStandardLibVersion);$(NetFrameworkLibVersion)
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Hosting/MongoDbGridFsSnapshotOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Akka.Configuration;
4 | using Akka.Hosting;
5 | using Akka.Persistence.Hosting;
6 | using Akka.Persistence.MongoDb.Snapshot;
7 |
8 | #nullable enable
9 | namespace Akka.Persistence.MongoDb.Hosting;
10 |
11 | public class MongoDbGridFsSnapshotOptions : MongoDbSnapshotOptions
12 | {
13 | public MongoDbGridFsSnapshotOptions() : this(true)
14 | {
15 | }
16 |
17 | public MongoDbGridFsSnapshotOptions(bool isDefault, string identifier = "mongodb") : base(isDefault, identifier)
18 | {
19 | }
20 |
21 | public override Type Type { get; } = typeof(MongoDbGridFsSnapshotStore);
22 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Hosting/MongoDbJournalOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Akka.Configuration;
4 | using Akka.Hosting;
5 | using Akka.Persistence.Hosting;
6 |
7 | #nullable enable
8 | namespace Akka.Persistence.MongoDb.Hosting;
9 |
10 | public class MongoDbJournalOptions : JournalOptions
11 | {
12 | private static readonly Config Default = MongoDbPersistence.DefaultConfiguration()
13 | .GetConfig(MongoDbJournalSettings.JournalConfigPath);
14 |
15 | public MongoDbJournalOptions() : this(true)
16 | {
17 | }
18 |
19 | public MongoDbJournalOptions(bool isDefaultPlugin, string identifier = "mongodb") : base(isDefaultPlugin)
20 | {
21 | Identifier = identifier;
22 | AutoInitialize = true;
23 | }
24 |
25 | ///
26 | /// Connection string used to access the MongoDb, also specifies the database.
27 | ///
28 | public string ConnectionString { get; set; } = "";
29 |
30 | ///
31 | /// Name of the collection for the event journal or snapshots
32 | ///
33 | public string? Collection { get; set; }
34 |
35 | ///
36 | /// Name of the collection for the event journal metadata
37 | ///
38 | public string? MetadataCollection { get; set; }
39 |
40 | ///
41 | /// Write Transaction
42 | ///
43 | public bool? UseWriteTransaction { get; set; }
44 |
45 | ///
46 | /// Read Transaction
47 | ///
48 | public bool? UseReadTransaction { get; set; }
49 |
50 | ///
51 | /// Set the batch size for read operations.
52 | /// If not set (null), this will use the default MongoDb behavior where all queries
53 | /// will have a batch size of 101 on the first page read and then will try to read
54 | /// as many documents as possible that fits the 16 MeBi limit.
55 | ///
56 | public int? ReadBatchSize { get; set; }
57 |
58 | ///
59 | /// When true, enables BSON serialization (which breaks features like Akka.Cluster.Sharding, AtLeastOnceDelivery, and so on.)
60 | ///
61 | public bool? LegacySerialization { get; set; }
62 |
63 | ///
64 | /// Timeout for individual database operations.
65 | ///
66 | ///
67 | /// Defaults to 10s.
68 | ///
69 | public TimeSpan? CallTimeout { get; set; }
70 |
71 | public override string Identifier { get; set; }
72 | protected override Config InternalDefaultConfig { get; } = Default;
73 |
74 | protected override StringBuilder Build(StringBuilder sb)
75 | {
76 | sb.AppendLine($"connection-string = {ConnectionString.ToHocon()}");
77 |
78 | if(Collection is not null)
79 | sb.AppendLine($"collection = {Collection.ToHocon()}");
80 |
81 | if(MetadataCollection is not null)
82 | sb.AppendLine($"metadata-collection = {MetadataCollection.ToHocon()}");
83 |
84 | if(CallTimeout is not null)
85 | sb.AppendLine($"call-timeout = {CallTimeout.ToHocon()}");
86 |
87 | if(UseWriteTransaction is not null)
88 | sb.AppendLine($"use-write-transaction = {UseWriteTransaction.ToHocon()}");
89 |
90 | if(UseReadTransaction is not null)
91 | sb.AppendLine($"use-read-transaction = {UseReadTransaction.ToHocon()}");
92 |
93 | sb.AppendLine($"read-batch-size = {(ReadBatchSize is not null ? ReadBatchSize.ToHocon() : "off")}");
94 |
95 | if(LegacySerialization is not null)
96 | sb.AppendLine($"legacy-serialization = {LegacySerialization.ToHocon()}");
97 |
98 | return base.Build(sb);
99 | }
100 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Hosting/MongoDbSnapshotOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Akka.Configuration;
4 | using Akka.Hosting;
5 | using Akka.Persistence.Hosting;
6 | using Akka.Persistence.MongoDb.Snapshot;
7 |
8 | #nullable enable
9 | namespace Akka.Persistence.MongoDb.Hosting;
10 |
11 | public class MongoDbSnapshotOptions : SnapshotOptions
12 | {
13 | private static readonly Config Default = MongoDbPersistence.DefaultConfiguration()
14 | .GetConfig(MongoDbSnapshotSettings.SnapshotStoreConfigPath);
15 |
16 | public MongoDbSnapshotOptions() : this(true)
17 | {
18 | }
19 |
20 | public MongoDbSnapshotOptions(bool isDefault, string identifier = "mongodb") : base(isDefault)
21 | {
22 | Identifier = identifier;
23 | AutoInitialize = true;
24 | }
25 |
26 | public virtual Type Type { get; } = typeof(MongoDbSnapshotStore);
27 |
28 | ///
29 | /// Connection string used to access the MongoDb, also specifies the database.
30 | ///
31 | public string ConnectionString { get; set; } = "";
32 |
33 | ///
34 | /// Name of the collection for the event journal or snapshots
35 | ///
36 | public string? Collection { get; set; }
37 |
38 | ///
39 | /// Write Transaction
40 | ///
41 | public bool? UseWriteTransaction { get; set; }
42 |
43 | ///
44 | /// Read Transaction
45 | ///
46 | public bool? UseReadTransaction { get; set; }
47 |
48 | ///
49 | /// When true, enables BSON serialization (which breaks features like Akka.Cluster.Sharding, AtLeastOnceDelivery, and so on.)
50 | ///
51 | public bool? LegacySerialization { get; set; }
52 |
53 | ///
54 | /// Timeout for individual database operations.
55 | ///
56 | ///
57 | /// Defaults to 10s.
58 | ///
59 | public TimeSpan? CallTimeout { get; set; }
60 |
61 | public override string Identifier { get; set; }
62 | protected override Config InternalDefaultConfig { get; } = Default;
63 |
64 | protected override StringBuilder Build(StringBuilder sb)
65 | {
66 | sb.AppendLine($"class = {Type.AssemblyQualifiedName.ToHocon()}");
67 |
68 | sb.AppendLine($"connection-string = {ConnectionString.ToHocon()}");
69 |
70 | if(UseWriteTransaction is not null)
71 | sb.AppendLine($"use-write-transaction = {UseWriteTransaction.ToHocon()}");
72 |
73 | if(UseReadTransaction is not null)
74 | sb.AppendLine($"use-read-transaction = {UseReadTransaction.ToHocon()}");
75 |
76 | if(Collection is not null)
77 | sb.AppendLine($"collection = {Collection.ToHocon()}");
78 |
79 | if(LegacySerialization is not null)
80 | sb.AppendLine($"legacy-serialization = {LegacySerialization.ToHocon()}");
81 |
82 | if(CallTimeout is not null)
83 | sb.AppendLine($"call-timeout = {CallTimeout.ToHocon()}");
84 |
85 | return base.Build(sb);
86 | }
87 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Hosting/README.md:
--------------------------------------------------------------------------------
1 | # Akka.Persistence.MongoDb.Hosting
2 |
3 | Akka.Hosting extension methods to add Akka.Persistence.MongoDb to an ActorSystem
4 |
5 | # Akka.Persistence.MongoDb Extension Methods
6 |
7 | ## WithMongoDbPersistence() Method
8 |
9 | ```csharp
10 | public static AkkaConfigurationBuilder WithMongoDbPersistence(
11 | this AkkaConfigurationBuilder builder,
12 | string connectionString,
13 | PersistenceMode mode = PersistenceMode.Both,
14 | bool autoInitialize = true,
15 | Action? journalBuilder = null,
16 | string pluginIdentifier = "mongodb",
17 | bool isDefaultPlugin = true);
18 | ```
19 |
20 | ```csharp
21 | public static AkkaConfigurationBuilder WithMongoDbPersistence(
22 | this AkkaConfigurationBuilder builder,
23 | Action? journalOptionConfigurator = null,
24 | Action? snapshotOptionConfigurator = null,
25 | bool isDefaultPlugin = true)
26 | ```
27 |
28 | ```csharp
29 | public static AkkaConfigurationBuilder WithMongoDbPersistence(
30 | this AkkaConfigurationBuilder builder,
31 | MongoDbJournalOptions? journalOptions = null,
32 | MongoDbSnapshotOptions? snapshotOptions = null)
33 | ```
34 |
35 | ### Parameters
36 |
37 | * `connectionString` __string__
38 |
39 | Connection string used for database access.
40 |
41 | * `mode` __PersistenceMode__
42 |
43 | Determines which settings should be added by this method call. __Default__: `PersistenceMode.Both`
44 |
45 | * `PersistenceMode.Journal`: Only add the journal settings
46 | * `PersistenceMode.SnapshotStore`: Only add the snapshot store settings
47 | * `PersistenceMode.Both`: Add both journal and snapshot store settings
48 |
49 | * `autoInitialize` __bool__
50 |
51 | Should the Mongo Db store collection be initialized automatically. __Default__: `false`
52 |
53 | * `journalBuilder` __Action\__
54 |
55 | An Action delegate used to configure an `AkkaPersistenceJournalBuilder` instance. Used to configure [Event Adapters](https://getakka.net/articles/persistence/event-adapters.html)
56 |
57 | * `journalConfigurator` __Action\__
58 |
59 | An Action delegate to configure a `MongoDbJournalOptions` instance.
60 |
61 | * `snapshotConfigurator` __Action\__
62 |
63 | An Action delegate to configure a `MongoDbSnapshotOptions` instance.
64 |
65 | * `journalOptions` __MongoDbJournalOptions__
66 |
67 | An `MongoDbJournalOptions` instance to configure the SqlServer journal.
68 |
69 | * `snapshotOptions` __MongoDbSnapshotOptions__
70 |
71 | An `MongoDbSnapshotOptions` instance to configure the SqlServer snapshot store.
72 |
73 | ## Example
74 |
75 | ```csharp
76 | using var host = new HostBuilder()
77 | .ConfigureServices((context, services) =>
78 | {
79 | services.AddAkka("mongoDbDemo", (builder, provider) =>
80 | {
81 | builder
82 | .WithMongoDbPersistence("your-mongodb-connection-string");
83 | });
84 | }).Build();
85 |
86 | await host.RunAsync();
87 | ```
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Akka.Persistence.MongoDb.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
6 |
7 | $(NetFrameworkTestVersion);$(NetTestVersion)
8 |
9 |
10 |
11 |
12 | $(NetTestVersion)
13 |
14 |
15 |
16 | $(DefineConstants);CICD
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | all
25 | runtime; build; native; contentfiles; analyzers; buildtransitive
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Bug25FixSpec.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Akka.Configuration;
7 | using Akka.Persistence.MongoDb.Journal;
8 | using Akka.Util.Internal;
9 | using Akka.Actor;
10 | using MongoDB.Driver;
11 | using Xunit;
12 | using Xunit.Abstractions;
13 |
14 | namespace Akka.Persistence.MongoDb.Tests
15 | {
16 | [Collection("MongoDbSpec")]
17 | public class Bug25FixSpec : Akka.TestKit.Xunit2.TestKit, IClassFixture
18 | {
19 | class MyJournalActor : ReceivePersistentActor
20 | {
21 | private readonly IActorRef _target;
22 |
23 | public MyJournalActor(string persistenceId, IActorRef target)
24 | {
25 | PersistenceId = persistenceId;
26 | _target = target;
27 |
28 | Recover(str =>
29 | {
30 | target.Tell(str);
31 | });
32 |
33 | Command(str =>
34 | {
35 | Persist(str, s =>
36 | {
37 | Sender.Tell(s);
38 | });
39 | });
40 | }
41 |
42 | public override string PersistenceId { get; }
43 | }
44 |
45 | private readonly MongoUrl _connectionString;
46 | private Lazy _database;
47 | public static readonly AtomicCounter Counter = new AtomicCounter(0);
48 | private readonly ITestOutputHelper _output;
49 |
50 | public Bug25FixSpec(ITestOutputHelper helper, DatabaseFixture fixture)
51 | : base(CreateSpecConfig(fixture, Counter.Current), output: helper)
52 | {
53 | _connectionString = new MongoUrl(fixture.MongoDbConnectionString(Counter.Current));
54 | Counter.IncrementAndGet();
55 | _output = helper;
56 | _database = new Lazy(() =>
57 | new MongoClient(_connectionString)
58 | .GetDatabase(_connectionString.DatabaseName));
59 | }
60 |
61 | [Fact(DisplayName = "Bugfix: Should be able to deserialize older entities written without manifests")]
62 | public async Task Should_deserialize_entries_without_manifest()
63 | {
64 | var persistenceId = "fuber";
65 | var props = Props.Create(() => new MyJournalActor(persistenceId, TestActor));
66 | var myPersistentActor = Sys.ActorOf(props);
67 | Watch(myPersistentActor);
68 |
69 | myPersistentActor.Tell("hit");
70 | ExpectMsg("hit");
71 |
72 | Sys.Stop(myPersistentActor);
73 | ExpectTerminated(myPersistentActor);
74 |
75 | var records = _database.Value.GetCollection("EventJournal");
76 |
77 | var builder = Builders.Filter;
78 | var filter = builder.Eq(x => x.PersistenceId, persistenceId);
79 | filter &= builder.Eq(x => x.SequenceNr, 1);
80 |
81 | var sort = Builders.Sort.Ascending(x => x.SequenceNr);
82 |
83 | AwaitCondition(() => records.CountDocuments(filter) > 0);
84 | var collections = await records
85 | .Find(filter)
86 | .Sort(sort)
87 | .ToListAsync();
88 |
89 | var entry = collections.Single();
90 | entry.Manifest = null; // null out the manifest
91 | await records.FindOneAndReplaceAsync(filter, entry);
92 |
93 | // recreate the persistent actor
94 | var myPersistentActor2 = Sys.ActorOf(props);
95 | ExpectMsg("hit"); // receive message upon recovery
96 | }
97 |
98 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id)
99 | {
100 | var specString = @"
101 | akka.test.single-expect-default = 10s
102 | akka.persistence {
103 | publish-plugin-commands = on
104 | journal {
105 | plugin = ""akka.persistence.journal.mongodb""
106 | mongodb {
107 | class = ""Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb""
108 | connection-string = """ + databaseFixture.MongoDbConnectionString(id) + @"""
109 | auto-initialize = on
110 | collection = ""EventJournal""
111 | }
112 | }
113 | }";
114 |
115 | return ConfigurationFactory.ParseString(specString)
116 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Bug61FixSpec.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 | using Akka.Configuration;
3 | using Akka.Persistence.Journal;
4 | using Akka.Persistence.MongoDb.Query;
5 | using Akka.Persistence.Query;
6 | using Akka.Streams;
7 | using Akka.Util.Internal;
8 | using FluentAssertions;
9 | using MongoDB.Driver.Core.Configuration;
10 | using MongoDB.Driver.Core.Misc;
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Collections.Immutable;
14 | using System.Linq;
15 | using System.Text;
16 | using System.Threading.Tasks;
17 | using Xunit;
18 | using Xunit.Abstractions;
19 |
20 | namespace Akka.Persistence.MongoDb.Tests
21 | {
22 | [Collection("MongoDbSpec")]
23 | public class Bug61FixSpec : Akka.TestKit.Xunit2.TestKit, IClassFixture
24 | {
25 | public static readonly AtomicCounter Counter = new AtomicCounter(0);
26 | private readonly ITestOutputHelper _output;
27 |
28 | protected MongoDbReadJournal ReadJournal { get; }
29 |
30 | protected IMaterializer Materializer { get; }
31 |
32 | public class RealMsg
33 | {
34 | public RealMsg(string msg)
35 | {
36 | Msg = msg;
37 | }
38 | public string Msg { get; }
39 | }
40 |
41 | public const int MessageCount = 20;
42 |
43 | public Bug61FixSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
44 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement()), "MongoDbCurrentEventsByTagSpec", output)
45 | {
46 | _output = output;
47 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
48 | Materializer = Sys.Materializer();
49 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
50 | }
51 |
52 | ///
53 | /// Reproduction spec for https://github.com/akkadotnet/Akka.Persistence.MongoDB/issues/61
54 | ///
55 | [Fact]
56 | public async Task Bug61_Events_Recovered_By_Id_Should_Match_Tag()
57 | {
58 | var actor = Sys.ActorOf(TagActor.Props("x"));
59 |
60 | actor.Tell(MessageCount);
61 | ExpectMsg($"{MessageCount}-done", TimeSpan.FromSeconds(20));
62 |
63 | var eventsById = await ReadJournal.CurrentEventsByPersistenceId("x", 0L, long.MaxValue)
64 | .RunAggregate(ImmutableHashSet.Empty, (agg, e) => agg.Add(e), Materializer);
65 |
66 | eventsById.Count.Should().Be(MessageCount);
67 |
68 | var eventsByTag = await ReadJournal.CurrentEventsByTag(typeof(RealMsg).Name)
69 | .RunAggregate(ImmutableHashSet.Empty, (agg, e) => agg.Add(e), Materializer);
70 |
71 | eventsByTag.Count.Should().Be(MessageCount);
72 |
73 | eventsById.All(x => x.Event is RealMsg).Should().BeTrue("Expected all events by id to be RealMsg");
74 | eventsByTag.All(x => x.Event is RealMsg).Should().BeTrue("Expected all events by tag to be RealMsg");
75 | }
76 |
77 | ///
78 | /// Reproduction spec for https://github.com/akkadotnet/Akka.Persistence.MongoDB/issues/80
79 | ///
80 | [Fact]
81 | public void Bug80_CurrentEventsByTag_should_Recover_until_end()
82 | {
83 | var actor = Sys.ActorOf(TagActor.Props("y"));
84 | //increased this to test for non-collision with the generated timestamps
85 | var msgCount = 5000;
86 | actor.Tell(msgCount);
87 | ExpectMsg($"{msgCount}-done", TimeSpan.FromSeconds(20));
88 |
89 | var eventsByTag = ReadJournal.CurrentEventsByTag(typeof(RealMsg).Name)
90 | .RunForeach(e => TestActor.Tell(e), Materializer);
91 |
92 | ReceiveN(msgCount);
93 | }
94 |
95 | ///
96 | /// Making sure EventsByTag didn't break during implementation of https://github.com/akkadotnet/Akka.Persistence.MongoDB/issues/80
97 | ///
98 | [Fact]
99 | public void Bug80_AllEventsByTag_should_Recover_all_messages()
100 | {
101 | var actor = Sys.ActorOf(TagActor.Props("y"));
102 | //increased this to test for non-collision with the generated timestamps
103 | var msgCount = 5000;
104 | actor.Tell(msgCount);
105 | ExpectMsg($"{msgCount}-done", TimeSpan.FromSeconds(20));
106 |
107 | var eventsByTag = ReadJournal.EventsByTag(typeof(RealMsg).Name)
108 | .RunForeach(e => TestActor.Tell(e), Materializer);
109 |
110 | // can't do this because Offset isn't IComparable
111 | // ReceiveN(msgCount).Cast().Select(x => x.Offset).Should().BeInAscendingOrder();
112 | ReceiveN(msgCount);
113 |
114 | // should receive more messages after the fact
115 | actor.Tell(msgCount);
116 | ExpectMsg($"{msgCount}-done", TimeSpan.FromSeconds(20));
117 | ReceiveN(msgCount);
118 | }
119 |
120 | private class TagActor : ReceivePersistentActor
121 | {
122 | public static Props Props(string id)
123 | {
124 | return Akka.Actor.Props.Create(() => new TagActor(id));
125 | }
126 |
127 | public TagActor(string id)
128 | {
129 | PersistenceId = id;
130 |
131 | Command(i =>
132 | {
133 | var msgs = new List();
134 | foreach (var n in Enumerable.Range(0, i))
135 | {
136 | msgs.Add(new RealMsg(i.ToString()));
137 | }
138 | PersistAll(msgs, m =>
139 | {
140 | if (LastSequenceNr >= i)
141 | {
142 | Sender.Tell($"{i}-done");
143 | }
144 | });
145 | });
146 |
147 | Command(r =>
148 | {
149 | Persist(r, e =>
150 | {
151 | Sender.Tell($"{e.Msg}-done");
152 | });
153 | });
154 | }
155 |
156 | public override string PersistenceId { get; }
157 | }
158 |
159 | private class EventTagger : IWriteEventAdapter
160 | {
161 | public string DefaultTag { get; }
162 |
163 | public EventTagger()
164 | {
165 | DefaultTag = "accounts";
166 | }
167 |
168 | public string Manifest(object evt)
169 | {
170 | return string.Empty;
171 | }
172 |
173 | public object ToJournal(object evt)
174 | {
175 | return new Tagged(evt, ImmutableHashSet.Empty.Add(DefaultTag).Add(evt.GetType().Name));
176 | }
177 | }
178 |
179 |
180 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id)
181 | {
182 | var specString = @"
183 | akka.test.single-expect-default = 10s
184 | akka.persistence {
185 | publish-plugin-commands = on
186 | journal {
187 | plugin = ""akka.persistence.journal.mongodb""
188 | mongodb {
189 | class = ""Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb""
190 | connection-string = """ + databaseFixture.MongoDbConnectionString(id) + @"""
191 | auto-initialize = on
192 | collection = ""EventJournal""
193 | event-adapters {
194 | tagger = """ + typeof(EventTagger).AssemblyQualifiedName + @"""
195 | }
196 | event-adapter-bindings = {
197 | """ + typeof(RealMsg).AssemblyQualifiedName + @""" = tagger
198 | }
199 | stored-as = binary
200 | }
201 | }
202 | query {
203 | mongodb {
204 | class = ""Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb""
205 | refresh-interval = 1s
206 | }
207 | }
208 | }";
209 |
210 | return ConfigurationFactory.ParseString(specString);
211 | }
212 | }
213 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/DatabaseFixture.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Util.Internal;
9 | using Mongo2Go;
10 | using MongoDB.Driver.Core.Misc;
11 | using System;
12 |
13 | namespace Akka.Persistence.MongoDb.Tests
14 | {
15 | public class DatabaseFixture : IDisposable
16 | {
17 | private MongoDbRunner _runner;
18 | public string ConnectionString { get; private set; }
19 |
20 | public DatabaseFixture()
21 | {
22 | _runner = MongoDbRunner.Start(singleNodeReplSet: true);
23 | //_runner = MongoDbRunner.Start();
24 | ConnectionString = ConString(_runner.ConnectionString);// + "akkanet";
25 | }
26 | public string MongoDbConnectionString(int id)
27 | {
28 | var s = ConnectionString.Split('?');
29 | var connectionString = s[0] + $"{id}?" + s[1];
30 | return connectionString;
31 | }
32 | private string ConString(string cString)
33 | {
34 | var s = cString.Split('?');
35 | var connectionString = s[0] + $"akkanet?" + s[1];
36 | return connectionString;
37 | }
38 | public void Dispose()
39 | {
40 | _runner.Dispose();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/GridFS/MongoDbGridFsLegacySerializationSnapshotStoreSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using System;
9 | using System.Diagnostics;
10 | using System.Security.Cryptography;
11 | using System.Threading.Tasks;
12 | using Akka.Configuration;
13 | using Akka.Event;
14 | using Akka.Persistence.TCK.Snapshot;
15 | using FluentAssertions;
16 | using Xunit;
17 | using Xunit.Abstractions;
18 |
19 | #nullable enable
20 | namespace Akka.Persistence.MongoDb.Tests.GridFS;
21 |
22 | [Collection("MongoDbSpec")]
23 | public class MongoDbGridFsLegacySerializationSnapshotStoreSpec : SnapshotStoreSpec, IClassFixture
24 | {
25 | protected override bool SupportsSerialization => false;
26 |
27 | public MongoDbGridFsLegacySerializationSnapshotStoreSpec(DatabaseFixture databaseFixture, ITestOutputHelper output)
28 | : base(CreateSpecConfig(databaseFixture), nameof(MongoDbGridFsLegacySerializationSnapshotStoreSpec), output)
29 | {
30 | Initialize();
31 | }
32 |
33 | protected override int SnapshotByteSizeLimit => 128 * 1024 * 1024;
34 |
35 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture)
36 | {
37 | var specString = @"
38 | akka.test.single-expect-default = 3s
39 | akka.persistence {
40 | publish-plugin-commands = on
41 | snapshot-store {
42 | plugin = ""akka.persistence.snapshot-store.mongodb""
43 | mongodb {
44 | class = ""Akka.Persistence.MongoDb.Snapshot.MongoDbGridFsSnapshotStore, Akka.Persistence.MongoDb""
45 | connection-string = """ + databaseFixture.ConnectionString + @"""
46 | auto-initialize = on
47 | collection = ""SnapshotStore""
48 | legacy-serialization = on
49 | }
50 | }
51 | }";
52 |
53 | return ConfigurationFactory.ParseString(specString)
54 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
55 | }
56 |
57 | [Fact]
58 | public async Task SnapshotStore_should_save_bigger_size_snapshot_consistently()
59 | {
60 | var metadata = new SnapshotMetadata(Pid, 100, DateTime.MinValue);
61 | var bigSnapshot = new byte[SnapshotByteSizeLimit];
62 | new Random().NextBytes(bigSnapshot);
63 | var senderProbe = CreateTestProbe();
64 | SnapshotStore.Tell(new SaveSnapshot(metadata, bigSnapshot), senderProbe.Ref);
65 | var saved = await senderProbe.ExpectMsgAsync();
66 |
67 | var stopwatch = Stopwatch.StartNew();
68 | SnapshotStore.Tell(
69 | new LoadSnapshot(Pid, new SnapshotSelectionCriteria(saved.Metadata.SequenceNr), long.MaxValue),
70 | senderProbe.Ref);
71 | var loaded = await senderProbe.ExpectMsgAsync();
72 | stopwatch.Stop();
73 | Log.Info($"{SnapshotByteSizeLimit} bytes snapshot loaded in {stopwatch.Elapsed.Milliseconds} milliseconds");
74 |
75 | MD5.Create().ComputeHash((byte[])loaded.Snapshot.Snapshot).Should()
76 | .BeEquivalentTo(MD5.Create().ComputeHash(bigSnapshot));
77 | }
78 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/GridFS/MongoDbGridFsSnapshotStoreSaveSnapshotSpec.cs:
--------------------------------------------------------------------------------
1 | using Akka.Configuration;
2 | using Akka.Persistence.TCK.Snapshot;
3 | using Xunit;
4 | using Xunit.Abstractions;
5 |
6 | namespace Akka.Persistence.MongoDb.Tests.GridFS;
7 |
8 | [Collection("MongoDbSpec")]
9 | public class MongoDbGridFsSnapshotStoreSaveSnapshotSpec: SnapshotStoreSaveSnapshotSpec, IClassFixture
10 | {
11 | public MongoDbGridFsSnapshotStoreSaveSnapshotSpec(DatabaseFixture databaseFixture, ITestOutputHelper output)
12 | : base(CreateSpecConfig(databaseFixture), nameof(MongoDbGridFsSnapshotStoreSaveSnapshotSpec), output)
13 | {
14 | }
15 |
16 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture)
17 | {
18 | var specString = $$"""
19 | akka.test.single-expect-default = 3s
20 | akka.persistence {
21 | publish-plugin-commands = on
22 | snapshot-store {
23 | plugin = "akka.persistence.snapshot-store.mongodb"
24 | mongodb {
25 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbGridFsSnapshotStore, Akka.Persistence.MongoDb"
26 | connection-string = "{{databaseFixture.ConnectionString}}"
27 | use-write-transaction = off
28 | auto-initialize = on
29 | collection = "SnapshotStore"
30 | }
31 | }
32 | }
33 | """;
34 |
35 | return ConfigurationFactory.ParseString(specString);
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/GridFS/MongoDbGridFsSnapshotStoreSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using System;
9 | using System.Diagnostics;
10 | using System.Security.Cryptography;
11 | using System.Threading.Tasks;
12 | using Akka.Configuration;
13 | using Akka.Event;
14 | using Akka.Persistence.TCK.Snapshot;
15 | using FluentAssertions;
16 | using Xunit;
17 | using Xunit.Abstractions;
18 |
19 | #nullable enable
20 | namespace Akka.Persistence.MongoDb.Tests.GridFS;
21 |
22 | [Collection("MongoDbSpec")]
23 | public class MongoDbGridFsSnapshotStoreSpec : SnapshotStoreSpec, IClassFixture
24 | {
25 | public MongoDbGridFsSnapshotStoreSpec(DatabaseFixture databaseFixture, ITestOutputHelper output)
26 | : base(CreateSpecConfig(databaseFixture), nameof(MongoDbGridFsSnapshotStoreSpec), output)
27 | {
28 | Initialize();
29 | }
30 |
31 | protected override int SnapshotByteSizeLimit => 128 * 1024 * 1024;
32 |
33 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture)
34 | {
35 | var specString = $$"""
36 | akka.test.single-expect-default = 3s
37 | akka.persistence {
38 | publish-plugin-commands = on
39 | snapshot-store {
40 | plugin = "akka.persistence.snapshot-store.mongodb"
41 | mongodb {
42 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbGridFsSnapshotStore, Akka.Persistence.MongoDb"
43 | connection-string = "{{databaseFixture.ConnectionString}}"
44 | use-write-transaction = off
45 | auto-initialize = on
46 | collection = "SnapshotStore"
47 | }
48 | }
49 | }
50 | """;
51 |
52 | return ConfigurationFactory.ParseString(specString);
53 | }
54 |
55 | [Fact]
56 | public async Task SnapshotStore_should_save_bigger_size_snapshot_consistently()
57 | {
58 | var metadata = new SnapshotMetadata(Pid, 100, DateTime.MinValue);
59 | var bigSnapshot = new byte[SnapshotByteSizeLimit];
60 | new Random().NextBytes(bigSnapshot);
61 | var senderProbe = CreateTestProbe();
62 | SnapshotStore.Tell(new SaveSnapshot(metadata, bigSnapshot), senderProbe.Ref);
63 | var saved = await senderProbe.ExpectMsgAsync();
64 |
65 | var stopwatch = Stopwatch.StartNew();
66 | SnapshotStore.Tell(
67 | new LoadSnapshot(Pid, new SnapshotSelectionCriteria(saved.Metadata.SequenceNr), long.MaxValue),
68 | senderProbe.Ref);
69 | var loaded = await senderProbe.ExpectMsgAsync();
70 | stopwatch.Stop();
71 | Log.Info($"{SnapshotByteSizeLimit} bytes snapshot loaded in {stopwatch.Elapsed.Milliseconds} milliseconds");
72 |
73 | MD5.Create().ComputeHash((byte[])loaded.Snapshot.Snapshot).Should()
74 | .BeEquivalentTo(MD5.Create().ComputeHash(bigSnapshot));
75 | }
76 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/GridFS/Serialization/MongoDbGridFsSnapshotStoreSerializationSpec.cs:
--------------------------------------------------------------------------------
1 | using Akka.Configuration;
2 | using Akka.Persistence.TCK.Serialization;
3 | using Akka.Util.Internal;
4 | using Xunit;
5 | using Xunit.Abstractions;
6 |
7 | #nullable enable
8 | namespace Akka.Persistence.MongoDb.Tests.GridFS.Serialization;
9 |
10 | [Collection("MongoDbSpec")]
11 | public class MongoDbSnapshotStoreSerializationSpec : SnapshotStoreSerializationSpec, IClassFixture
12 | {
13 | public static readonly AtomicCounter Counter = new AtomicCounter(0);
14 |
15 | private readonly ITestOutputHelper _output;
16 |
17 | public MongoDbSnapshotStoreSerializationSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
18 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement()), nameof(MongoDbSnapshotStoreSerializationSpec), output)
19 | {
20 | _output = output;
21 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
22 | }
23 |
24 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id)
25 | {
26 | var specString = @"
27 | akka.test.single-expect-default = 3s
28 | akka.persistence {
29 | publish-plugin-commands = on
30 | snapshot-store {
31 | plugin = ""akka.persistence.snapshot-store.mongodb""
32 | mongodb {
33 | class = ""Akka.Persistence.MongoDb.Snapshot.MongoDbGridFsSnapshotStore, Akka.Persistence.MongoDb""
34 | connection-string = """ + databaseFixture.MongoDbConnectionString(id) + @"""
35 | auto-initialize = on
36 | collection = ""SnapshotStore""
37 | }
38 | }
39 | }";
40 |
41 | return ConfigurationFactory.ParseString(specString);
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbJournalOptionsSpec.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using Akka.Configuration;
5 | using Akka.Persistence.MongoDb.Hosting;
6 | using FluentAssertions;
7 | using FluentAssertions.Extensions;
8 | using Microsoft.Extensions.Configuration;
9 | using Xunit;
10 |
11 | namespace Akka.Persistence.MongoDb.Tests.Hosting
12 | {
13 | public class MongoDbJournalOptionsSpec
14 | {
15 | [Fact(DisplayName = "MongoDbJournalOptions as default plugin should generate plugin setting")]
16 | public void DefaultPluginJournalOptionsTest()
17 | {
18 | var options = new MongoDbJournalOptions(true);
19 | var config = options.ToConfig();
20 |
21 | config.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.mongodb");
22 | config.HasPath("akka.persistence.journal.mongodb").Should().BeTrue();
23 | }
24 |
25 | [Fact(DisplayName = "Empty MongoDbJournalOptions should equal empty config with default fallback")]
26 | public void DefaultJournalOptionsTest()
27 | {
28 | var options = new MongoDbJournalOptions(false);
29 | var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
30 | var baseRootConfig = Config.Empty
31 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
32 |
33 | emptyRootConfig.GetString("akka.persistence.journal.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.journal.plugin"));
34 |
35 | var config = emptyRootConfig.GetConfig("akka.persistence.journal.mongodb");
36 | var baseConfig = baseRootConfig.GetConfig("akka.persistence.journal.mongodb");
37 | config.Should().NotBeNull();
38 | baseConfig.Should().NotBeNull();
39 |
40 | config.GetString("class").Should().Be(baseConfig.GetString("class"));
41 | config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
42 | config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
43 | config.GetBoolean("use-read-transaction").Should().Be(baseConfig.GetBoolean("use-read-transaction"));
44 | config.GetBoolean("read-batch-size").Should().Be(baseConfig.GetBoolean("read-batch-size"));
45 | config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
46 | config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
47 | config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
48 | config.GetString("metadata-collection").Should().Be(baseConfig.GetString("metadata-collection"));
49 | config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
50 | config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
51 | }
52 |
53 | [Fact(DisplayName = "Empty MongoDbJournalOptions with custom identifier should equal empty config with default fallback")]
54 | public void CustomIdJournalOptionsTest()
55 | {
56 | var options = new MongoDbJournalOptions(false, "custom");
57 | var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
58 | var baseRootConfig = Config.Empty
59 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
60 |
61 | emptyRootConfig.GetString("akka.persistence.journal.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.journal.plugin"));
62 |
63 | var config = emptyRootConfig.GetConfig("akka.persistence.journal.custom");
64 | var baseConfig = baseRootConfig.GetConfig("akka.persistence.journal.mongodb");
65 | config.Should().NotBeNull();
66 | baseConfig.Should().NotBeNull();
67 |
68 | config.GetString("class").Should().Be(baseConfig.GetString("class"));
69 | config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
70 | config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
71 | config.GetBoolean("use-read-transaction").Should().Be(baseConfig.GetBoolean("use-read-transaction"));
72 | config.GetBoolean("read-batch-size").Should().Be(baseConfig.GetBoolean("read-batch-size"));
73 | config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
74 | config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
75 | config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
76 | config.GetString("metadata-collection").Should().Be(baseConfig.GetString("metadata-collection"));
77 | config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
78 | config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
79 | }
80 |
81 | [Fact(DisplayName = "MongoDbJournalOptions should generate proper config")]
82 | public void JournalOptionsTest()
83 | {
84 | var options = new MongoDbJournalOptions(true)
85 | {
86 | Identifier = "custom",
87 | AutoInitialize = true,
88 | ConnectionString = "testConnection",
89 | Collection = "testCollection",
90 | MetadataCollection = "metadataCollection",
91 | UseWriteTransaction = true,
92 | UseReadTransaction = true,
93 | ReadBatchSize = 16,
94 | LegacySerialization = true,
95 | CallTimeout = TimeSpan.FromHours(2)
96 | };
97 |
98 | var baseConfig = options.ToConfig();
99 |
100 | baseConfig.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.custom");
101 |
102 | var config = baseConfig.GetConfig("akka.persistence.journal.custom");
103 | config.Should().NotBeNull();
104 | config.GetString("connection-string").Should().Be(options.ConnectionString);
105 | config.GetBoolean("auto-initialize").Should().Be(options.AutoInitialize);
106 | config.GetString("collection").Should().Be(options.Collection);
107 | config.GetString("metadata-collection").Should().Be(options.MetadataCollection);
108 | config.GetBoolean("use-write-transaction").Should().Be(options.UseWriteTransaction.Value);
109 | config.GetBoolean("use-read-transaction").Should().Be(options.UseReadTransaction.Value);
110 | config.GetString("read-batch-size").ToLowerInvariant().Should().NotBe("off");
111 | config.GetString("read-batch-size").ToLowerInvariant().Should().NotBe("false");
112 | config.GetInt("read-batch-size").Should().Be(options.ReadBatchSize.Value);
113 | config.GetBoolean("legacy-serialization").Should().Be(options.LegacySerialization.Value);
114 | config.GetTimeSpan("call-timeout").Should().Be(options.CallTimeout.Value);
115 | }
116 |
117 | const string Json = @"
118 | {
119 | ""Logging"": {
120 | ""LogLevel"": {
121 | ""Default"": ""Information"",
122 | ""Microsoft.AspNetCore"": ""Warning""
123 | }
124 | },
125 | ""Akka"": {
126 | ""JournalOptions"": {
127 | ""ConnectionString"": ""mongodb://localhost:27017"",
128 | ""UseWriteTransaction"": ""true"",
129 | ""UseReadTransaction"": ""true"",
130 | ""ReadBatchSize"": 16,
131 | ""Identifier"": ""custommongodb"",
132 | ""AutoInitialize"": true,
133 | ""IsDefaultPlugin"": false,
134 | ""Collection"": ""CustomEnventJournalCollection"",
135 | ""MetadataCollection"": ""CustomMetadataCollection"",
136 | ""LegacySerialization"" : ""true"",
137 | ""CallTimeout"": ""00:10:00"",
138 | ""Serializer"": ""hyperion"",
139 | }
140 | }
141 | }";
142 |
143 | [Fact(DisplayName = "MongoDbJournalOptions should be bindable to IConfiguration")]
144 | public void JournalOptionsIConfigurationBindingTest()
145 | {
146 | using var stream = new MemoryStream(Encoding.UTF8.GetBytes(Json));
147 | var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();
148 |
149 | var options = jsonConfig.GetSection("Akka:JournalOptions").Get();
150 | options.ConnectionString.Should().Be("mongodb://localhost:27017");
151 | options.UseWriteTransaction.Should().BeTrue();
152 | options.UseReadTransaction.Should().BeTrue();
153 | options.ReadBatchSize.Should().Be(16);
154 | options.Identifier.Should().Be("custommongodb");
155 | options.AutoInitialize.Should().BeTrue();
156 | options.IsDefaultPlugin.Should().BeFalse();
157 | options.Collection.Should().Be("CustomEnventJournalCollection");
158 | options.MetadataCollection.Should().Be("CustomMetadataCollection");
159 | options.LegacySerialization.Should().BeTrue();
160 | options.CallTimeout.Should().Be(10.Minutes());
161 | options.Serializer.Should().Be("hyperion");
162 | }
163 | }
164 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Hosting/MongoDbSnapshotOptionsSpec.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using Akka.Configuration;
5 | using Akka.Persistence.MongoDb.Hosting;
6 | using FluentAssertions;
7 | using FluentAssertions.Extensions;
8 | using Microsoft.Extensions.Configuration;
9 | using Xunit;
10 |
11 | namespace Akka.Persistence.MongoDb.Tests.Hosting
12 | {
13 | public class MongoDbSnapshotOptionsSpec
14 | {
15 | [Fact(DisplayName = "MongoDbSnapshotOptions as default plugin should generate plugin setting")]
16 | public void DefaultPluginSnapshotOptionsTest()
17 | {
18 | var options = new MongoDbSnapshotOptions(true);
19 | var config = options.ToConfig();
20 |
21 | config.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.mongodb");
22 | config.HasPath("akka.persistence.snapshot-store.mongodb").Should().BeTrue();
23 | }
24 |
25 | [Fact(DisplayName = "Empty MongoDbSnapshotOptions with default fallback should return default config")]
26 | public void DefaultSnapshotOptionsTest()
27 | {
28 | var options = new MongoDbSnapshotOptions(false);
29 | var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
30 | var baseRootConfig = Config.Empty
31 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
32 |
33 | emptyRootConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.snapshot-store.plugin"));
34 |
35 | var config = emptyRootConfig.GetConfig("akka.persistence.snapshot-store.mongodb");
36 | var baseConfig = baseRootConfig.GetConfig("akka.persistence.snapshot-store.mongodb");
37 | config.Should().NotBeNull();
38 | baseConfig.Should().NotBeNull();
39 |
40 | Type.GetType(config.GetString("class")).Should().Be(Type.GetType(baseConfig.GetString("class")));
41 | config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
42 | config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
43 | config.GetBoolean("use-read-transaction").Should().Be(baseConfig.GetBoolean("use-read-transaction"));
44 | config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
45 | config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
46 | config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
47 | config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
48 | config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
49 | }
50 |
51 | [Fact(DisplayName = "Empty MongoDbSnapshotOptions with custom identifier should equal empty config with default fallback")]
52 | public void CustomIdSnapshotOptionsTest()
53 | {
54 | var options = new MongoDbSnapshotOptions(false, "custom");
55 | var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
56 | var baseRootConfig = Config.Empty
57 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
58 |
59 | emptyRootConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.snapshot-store.plugin"));
60 |
61 | var config = emptyRootConfig.GetConfig("akka.persistence.snapshot-store.custom");
62 | var baseConfig = baseRootConfig.GetConfig("akka.persistence.snapshot-store.mongodb");
63 | config.Should().NotBeNull();
64 | baseConfig.Should().NotBeNull();
65 |
66 | Type.GetType(config.GetString("class")).Should().Be(Type.GetType(baseConfig.GetString("class")));
67 | config.GetString("connection-string").Should().Be(baseConfig.GetString("connection-string"));
68 | config.GetBoolean("use-write-transaction").Should().Be(baseConfig.GetBoolean("use-write-transaction"));
69 | config.GetBoolean("use-read-transaction").Should().Be(baseConfig.GetBoolean("use-read-transaction"));
70 | config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
71 | config.GetString("plugin-dispatcher").Should().Be(baseConfig.GetString("plugin-dispatcher"));
72 | config.GetString("collection").Should().Be(baseConfig.GetString("collection"));
73 | config.GetBoolean("legacy-serialization").Should().Be(baseConfig.GetBoolean("legacy-serialization"));
74 | config.GetTimeSpan("call-timeout").Should().Be(baseConfig.GetTimeSpan("call-timeout"));
75 | }
76 |
77 | [Fact(DisplayName = "MongoDbSnapshotOptions should generate proper config")]
78 | public void SnapshotOptionsTest()
79 | {
80 | var options = new MongoDbSnapshotOptions(true)
81 | {
82 | Identifier = "custom",
83 | AutoInitialize = true,
84 | ConnectionString = "testConnection",
85 | Collection = "testCollection",
86 | UseWriteTransaction = true,
87 | UseReadTransaction = true,
88 | LegacySerialization = true,
89 | CallTimeout = TimeSpan.FromHours(2)
90 | };
91 |
92 | var baseConfig = options.ToConfig()
93 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
94 |
95 | baseConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.custom");
96 |
97 | var config = baseConfig.GetConfig("akka.persistence.snapshot-store.custom");
98 | config.Should().NotBeNull();
99 | config.GetString("connection-string").Should().Be(options.ConnectionString);
100 | config.GetBoolean("auto-initialize").Should().Be(options.AutoInitialize);
101 | config.GetString("collection").Should().Be(options.Collection);
102 | config.GetBoolean("use-write-transaction").Should().Be(options.UseWriteTransaction.Value);
103 | config.GetBoolean("use-read-transaction").Should().Be(options.UseReadTransaction.Value);
104 | config.GetBoolean("legacy-serialization").Should().Be(options.LegacySerialization.Value);
105 | config.GetTimeSpan("call-timeout").Should().Be(options.CallTimeout.Value);
106 | }
107 |
108 | [Fact(DisplayName = "MongoDbSnapshotOptions should be bindable to IConfiguration")]
109 | public void SnapshotOptionsIConfigurationBindingTest()
110 | {
111 | const string json = @"
112 | {
113 | ""Logging"": {
114 | ""LogLevel"": {
115 | ""Default"": ""Information"",
116 | ""Microsoft.AspNetCore"": ""Warning""
117 | }
118 | },
119 | ""Akka"": {
120 | ""SnapshotOptions"": {
121 | ""ConnectionString"": ""mongodb://localhost:27017"",
122 | ""UseWriteTransaction"": ""true"",
123 | ""UseReadTransaction"": ""true"",
124 | ""Identifier"": ""custommongodb"",
125 | ""AutoInitialize"": true,
126 | ""IsDefaultPlugin"": false,
127 | ""Collection"": ""CustomEnventJournalCollection"",
128 | ""LegacySerialization"" : ""true"",
129 | ""CallTimeout"": ""00:10:00"",
130 | ""Serializer"": ""hyperion"",
131 | }
132 | }
133 | }";
134 |
135 | using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
136 | var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();
137 |
138 | var options = jsonConfig.GetSection("Akka:SnapshotOptions").Get();
139 | options.ConnectionString.Should().Be("mongodb://localhost:27017");
140 | options.UseWriteTransaction.Should().BeTrue();
141 | options.UseReadTransaction.Should().BeTrue();
142 | options.Identifier.Should().Be("custommongodb");
143 | options.AutoInitialize.Should().BeTrue();
144 | options.IsDefaultPlugin.Should().BeFalse();
145 | options.Collection.Should().Be("CustomEnventJournalCollection");
146 | options.LegacySerialization.Should().BeTrue();
147 | options.CallTimeout.Should().Be(10.Minutes());
148 | options.Serializer.Should().Be("hyperion");
149 | }
150 | }
151 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/JournalTestActor.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace Akka.Persistence.MongoDb.Tests
7 | {
8 | public class JournalTestActor : UntypedPersistentActor
9 | {
10 | public static Props Props(string persistenceId) => Actor.Props.Create(() => new JournalTestActor(persistenceId));
11 |
12 | public sealed class DeleteCommand
13 | {
14 | public DeleteCommand(long toSequenceNr)
15 | {
16 | ToSequenceNr = toSequenceNr;
17 | }
18 |
19 | public long ToSequenceNr { get; }
20 | }
21 |
22 | public JournalTestActor(string persistenceId)
23 | {
24 | PersistenceId = persistenceId;
25 | }
26 |
27 | public override string PersistenceId { get; }
28 |
29 | protected override void OnRecover(object message)
30 | {
31 | }
32 |
33 | protected override void OnCommand(object message)
34 | {
35 | switch (message) {
36 | case DeleteCommand delete:
37 | DeleteMessages(delete.ToSequenceNr);
38 | Sender.Tell($"{delete.ToSequenceNr}-deleted");
39 | break;
40 | case string cmd:
41 | var sender = Sender;
42 | Persist(cmd, e => sender.Tell($"{e}-done"));
43 | break;
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbAllEventsSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2017 Akka.NET Contrib
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using Akka.Configuration;
8 | using Akka.Persistence.MongoDb.Query;
9 | using Akka.Persistence.Query;
10 | using Akka.Persistence.TCK.Query;
11 | using Akka.Util.Internal;
12 | using Xunit;
13 | using Xunit.Abstractions;
14 |
15 | namespace Akka.Persistence.MongoDb.Tests
16 | {
17 | [Collection("MongoDbSpec")]
18 | public class MongoDbTransactionAllEventsSpec : MongoDbAllEventsSpecBase
19 | {
20 | public MongoDbTransactionAllEventsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, true)
21 | {
22 | }
23 | }
24 |
25 | [Collection("MongoDbSpec")]
26 | public class MongoDbAllEventsSpec : MongoDbAllEventsSpecBase
27 | {
28 | public MongoDbAllEventsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, false)
29 | {
30 | }
31 | }
32 |
33 | public abstract class MongoDbAllEventsSpecBase: AllEventsSpec, IClassFixture
34 | {
35 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
36 | {
37 | // akka.test.single-expect-default = 10s
38 | var specString = $$"""
39 | akka.test.single-expect-default = 10s
40 | akka.persistence {
41 | publish-plugin-commands = on
42 | journal {
43 | plugin = "akka.persistence.journal.mongodb"
44 | mongodb {
45 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
46 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
47 | use-write-transaction = {{(transaction ? "on" : "off")}}
48 | auto-initialize = on
49 | collection = "EventJournal"
50 | }
51 | }
52 | snapshot-store {
53 | plugin = "akka.persistence.snapshot-store.mongodb"
54 | mongodb {
55 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
56 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
57 | use-write-transaction = {{(transaction ? "on" : "off")}}
58 | }
59 | }
60 | query {
61 | mongodb {
62 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
63 | refresh-interval = 1s
64 | }
65 | }
66 | }
67 | """;
68 |
69 | return ConfigurationFactory.ParseString(specString);
70 | }
71 |
72 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
73 |
74 | protected MongoDbAllEventsSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
75 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbAllEventsSpec", output)
76 | {
77 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbCurrentAllEventsSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2017 Akka.NET Contrib
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using Akka.Configuration;
8 | using Akka.Persistence.MongoDb.Query;
9 | using Akka.Persistence.Query;
10 | using Akka.Persistence.TCK.Query;
11 | using Akka.Util.Internal;
12 | using Xunit;
13 | using Xunit.Abstractions;
14 |
15 | namespace Akka.Persistence.MongoDb.Tests
16 | {
17 | [Collection("MongoDbSpec")]
18 | public class MongoDbTransactionCurrentAllEventsSpec : MongoDbCurrentAllEventsSpecBase
19 | {
20 | public MongoDbTransactionCurrentAllEventsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, true)
21 | {
22 | }
23 | }
24 |
25 | [Collection("MongoDbSpec")]
26 | public class MongoDbCurrentAllEventsSpec : MongoDbCurrentAllEventsSpecBase
27 | {
28 | public MongoDbCurrentAllEventsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, false)
29 | {
30 | }
31 | }
32 |
33 | public abstract class MongoDbCurrentAllEventsSpecBase : CurrentAllEventsSpec, IClassFixture
34 | {
35 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
36 | {
37 | // akka.test.single-expect-default = 10s
38 | var specString = $$"""
39 | akka.test.single-expect-default = 10s
40 | akka.persistence {
41 | publish-plugin-commands = on
42 | journal {
43 | plugin = "akka.persistence.journal.mongodb"
44 | mongodb {
45 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
46 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
47 | use-write-transaction = {{(transaction ? "on" : "off")}}
48 | auto-initialize = on
49 | collection = "EventJournal"
50 | }
51 | }
52 | snapshot-store {
53 | plugin = "akka.persistence.snapshot-store.mongodb"
54 | mongodb {
55 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
56 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
57 | use-write-transaction = {{(transaction ? "on" : "off")}}
58 | }
59 | }
60 | query {
61 | mongodb {
62 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
63 | refresh-interval = 1s
64 | }
65 | }
66 | }
67 | """;
68 |
69 | return ConfigurationFactory.ParseString(specString);
70 | }
71 |
72 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
73 |
74 | protected MongoDbCurrentAllEventsSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
75 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbCurrentAllEventsSpec", output)
76 | {
77 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbCurrentEventsByPersistenceIdsSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 | using Akka.Persistence.MongoDb.Query;
12 | using Akka.Persistence.Query;
13 | using Xunit.Abstractions;
14 | using Akka.Util.Internal;
15 |
16 | namespace Akka.Persistence.MongoDb.Tests
17 | {
18 | [Collection("MongoDbSpec")]
19 | public class MongoDbTransactionCurrentEventsByPersistenceIdsSpec: MongoDbCurrentEventsByPersistenceIdsSpecBase
20 | {
21 | public MongoDbTransactionCurrentEventsByPersistenceIdsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
22 | : base(output, databaseFixture, true)
23 | {
24 | }
25 | }
26 |
27 | [Collection("MongoDbSpec")]
28 | public class MongoDbCurrentEventsByPersistenceIdsSpec: MongoDbCurrentEventsByPersistenceIdsSpecBase
29 | {
30 | public MongoDbCurrentEventsByPersistenceIdsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
31 | : base(output, databaseFixture, false)
32 | {
33 | }
34 | }
35 |
36 | public abstract class MongoDbCurrentEventsByPersistenceIdsSpecBase : TCK.Query.CurrentEventsByPersistenceIdSpec, IClassFixture
37 | {
38 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
39 | private readonly ITestOutputHelper _output;
40 |
41 | protected MongoDbCurrentEventsByPersistenceIdsSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
42 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbCurrentEventsByPersistenceIdsSpec", output)
43 | {
44 | _output = output;
45 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
46 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
47 | }
48 |
49 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
50 | {
51 | var specString = $$"""
52 | akka.test.single-expect-default = 10s
53 | akka.persistence {
54 | publish-plugin-commands = on
55 | journal {
56 | plugin = "akka.persistence.journal.mongodb"
57 | mongodb {
58 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
59 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
60 | use-write-transaction = {{(transaction ? "on" : "off")}}
61 | auto-initialize = on
62 | collection = "EventJournal"
63 | }
64 | }
65 | snapshot-store {
66 | plugin = "akka.persistence.snapshot-store.mongodb"
67 | mongodb {
68 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
69 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
70 | use-write-transaction = {{(transaction ? "on" : "off")}}
71 | }
72 | }
73 | query {
74 | mongodb {
75 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
76 | refresh-interval = 1s
77 | }
78 | }
79 | }
80 | """;
81 |
82 | return ConfigurationFactory.ParseString(specString);
83 | }
84 |
85 | }
86 |
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbCurrentEventsByTagSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 | using Akka.Persistence.MongoDb.Query;
12 | using Akka.Persistence.Query;
13 | using Xunit.Abstractions;
14 | using Akka.Util.Internal;
15 | using System;
16 | using Akka.Actor;
17 | using Akka.Streams.TestKit;
18 | using System.Linq;
19 | using System.Diagnostics;
20 |
21 | namespace Akka.Persistence.MongoDb.Tests
22 | {
23 | [Collection("MongoDbSpec")]
24 | public class MongoDbTransactionCurrentEventsByTagSpec : MongoDbCurrentEventsByTagSpecBase
25 | {
26 | public MongoDbTransactionCurrentEventsByTagSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, true)
27 | {
28 | }
29 | }
30 |
31 | [Collection("MongoDbSpec")]
32 | public class MongoDbCurrentEventsByTagSpec : MongoDbCurrentEventsByTagSpecBase
33 | {
34 | public MongoDbCurrentEventsByTagSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, false)
35 | {
36 | }
37 | }
38 |
39 | public abstract class MongoDbCurrentEventsByTagSpecBase : Akka.Persistence.TCK.Query.CurrentEventsByTagSpec, IClassFixture
40 | {
41 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
42 | private readonly ITestOutputHelper _output;
43 |
44 | protected MongoDbCurrentEventsByTagSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
45 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbCurrentEventsByTagSpec", output)
46 | {
47 |
48 | _output = output;
49 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
50 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
51 |
52 | var x = Sys.ActorOf(TestActor.Props("x"));
53 | x.Tell("warm-up");
54 | ExpectMsg("warm-up-done", TimeSpan.FromSeconds(10));
55 | }
56 |
57 | protected override bool SupportsTagsInEventEnvelope => true;
58 |
59 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
60 | {
61 | var specString = $$"""
62 | akka.test.single-expect-default = 3s
63 | akka.persistence {
64 | publish-plugin-commands = on
65 | journal {
66 | plugin = "akka.persistence.journal.mongodb"
67 | mongodb {
68 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
69 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
70 | use-write-transaction = {{(transaction ? "on" : "off")}}
71 | auto-initialize = on
72 | collection = "EventJournal"
73 | event-adapters {
74 | color-tagger = "Akka.Persistence.TCK.Query.ColorFruitTagger, Akka.Persistence.TCK"
75 | }
76 | event-adapter-bindings = {
77 | "System.String" = color-tagger
78 | }
79 | }
80 | }
81 | snapshot-store {
82 | plugin = "akka.persistence.snapshot-store.mongodb"
83 | mongodb {
84 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
85 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
86 | use-write-transaction = {{(transaction ? "on" : "off")}}
87 | }
88 | }
89 | query {
90 | mongodb {
91 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
92 | refresh-interval = 1s
93 | }
94 | }
95 | }
96 | """;
97 |
98 | return ConfigurationFactory.ParseString(specString);
99 | }
100 |
101 | internal class TestActor : UntypedPersistentActor
102 | {
103 | public static Props Props(string persistenceId) => Actor.Props.Create(() => new TestActor(persistenceId));
104 |
105 | public sealed class DeleteCommand
106 | {
107 | public DeleteCommand(long toSequenceNr)
108 | {
109 | ToSequenceNr = toSequenceNr;
110 | }
111 |
112 | public long ToSequenceNr { get; }
113 | }
114 |
115 | public TestActor(string persistenceId)
116 | {
117 | PersistenceId = persistenceId;
118 | }
119 |
120 | public override string PersistenceId { get; }
121 |
122 | protected override void OnRecover(object message)
123 | {
124 | }
125 |
126 | protected override void OnCommand(object message)
127 | {
128 | switch (message) {
129 | case DeleteCommand delete:
130 | DeleteMessages(delete.ToSequenceNr);
131 | Sender.Tell($"{delete.ToSequenceNr}-deleted");
132 | break;
133 | case string cmd:
134 | var sender = Sender;
135 | Persist(cmd, e => sender.Tell($"{e}-done"));
136 | break;
137 | }
138 | }
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbCurrentPersistenceIdsSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 | using Akka.Persistence.MongoDb.Query;
12 | using Akka.Persistence.Query;
13 | using Xunit.Abstractions;
14 | using Akka.Util.Internal;
15 | using System;
16 | using Akka.Actor;
17 | using Akka.Streams.TestKit;
18 | using System.Linq;
19 | using System.Diagnostics;
20 |
21 | namespace Akka.Persistence.MongoDb.Tests
22 | {
23 | [Collection("MongoDbSpec")]
24 | public class MongoDbTransactionCurrentPersistenceIdsSpec : MongoDbCurrentPersistenceIdsSpecBase
25 | {
26 | public MongoDbTransactionCurrentPersistenceIdsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, true)
27 | {
28 | }
29 | }
30 |
31 | [Collection("MongoDbSpec")]
32 | public class MongoDbCurrentPersistenceIdsSpec : MongoDbCurrentPersistenceIdsSpecBase
33 | {
34 | public MongoDbCurrentPersistenceIdsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, false)
35 | {
36 | }
37 | }
38 |
39 | public abstract class MongoDbCurrentPersistenceIdsSpecBase : Akka.Persistence.TCK.Query.CurrentPersistenceIdsSpec, IClassFixture
40 | {
41 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
42 | private readonly ITestOutputHelper _output;
43 |
44 | protected MongoDbCurrentPersistenceIdsSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
45 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbCurrentPersistenceIdsSpec", output)
46 | {
47 | _output = output;
48 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
49 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
50 | }
51 |
52 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
53 | {
54 | var specString = $$"""
55 | akka.test.single-expect-default = 3s
56 | akka.persistence {
57 | publish-plugin-commands = on
58 | journal {
59 | plugin = "akka.persistence.journal.mongodb"
60 | mongodb {
61 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
62 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
63 | use-write-transaction = {{(transaction ? "on" : "off")}}
64 | auto-initialize = on
65 | collection = "EventJournal"
66 | }
67 | }
68 | query {
69 | mongodb {
70 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
71 | refresh-interval = 1s
72 | }
73 | }
74 | }
75 | """;
76 |
77 | return ConfigurationFactory.ParseString(specString);
78 | }
79 |
80 | public override void ReadJournal_query_CurrentPersistenceIds_should_not_see_new_events_after_complete()
81 | {
82 | var queries = ReadJournal.AsInstanceOf();
83 |
84 | Setup("a", 1);
85 | Setup("b", 1);
86 | Setup("c", 1);
87 |
88 | var greenSrc = queries.CurrentPersistenceIds();
89 | var probe = greenSrc.RunWith(this.SinkProbe(), Materializer);
90 | var firstTwo = probe.Request(2).ExpectNextN(2);
91 | Assert.Empty(firstTwo.Except(new[] { "a", "b", "c" }).ToArray());
92 |
93 | var last = new[] { "a", "b", "c" }.Except(firstTwo).First();
94 | Setup("d", 1);
95 |
96 | probe.ExpectNoMsg(TimeSpan.FromMilliseconds(100));
97 | probe.Request(5)
98 | .ExpectNext(last)
99 | .ExpectComplete();
100 | }
101 |
102 | private IActorRef Setup(string persistenceId, int n)
103 | {
104 | var sw = Stopwatch.StartNew();
105 | var pref = Sys.ActorOf(JournalTestActor.Props(persistenceId));
106 | for (int i = 1; i <= n; i++) {
107 | pref.Tell($"{persistenceId}-{i}");
108 | ExpectMsg($"{persistenceId}-{i}-done", TimeSpan.FromSeconds(10), $"{persistenceId}-{i}-done");
109 | }
110 | _output.WriteLine(sw.ElapsedMilliseconds.ToString());
111 | return pref;
112 | }
113 |
114 | }
115 |
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbEventsByPersistenceIdSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 | using Akka.Persistence.MongoDb.Query;
12 | using Akka.Persistence.Query;
13 | using Xunit.Abstractions;
14 | using Akka.Util.Internal;
15 |
16 | namespace Akka.Persistence.MongoDb.Tests
17 | {
18 | [Collection("MongoDbSpec")]
19 | public class MongoDbTransactionEventsByPersistenceIdSpec : MongoDbEventsByPersistenceIdSpecBase
20 | {
21 | public MongoDbTransactionEventsByPersistenceIdSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, true)
22 | {
23 | }
24 | }
25 |
26 | [Collection("MongoDbSpec")]
27 | public class MongoDbEventsByPersistenceIdSpec : MongoDbEventsByPersistenceIdSpecBase
28 | {
29 | public MongoDbEventsByPersistenceIdSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, false)
30 | {
31 | }
32 | }
33 |
34 | public abstract class MongoDbEventsByPersistenceIdSpecBase : Akka.Persistence.TCK.Query.EventsByPersistenceIdSpec, IClassFixture
35 | {
36 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
37 | private readonly ITestOutputHelper _output;
38 |
39 | protected MongoDbEventsByPersistenceIdSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
40 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbEventsByPersistenceIdSpec", output)
41 | {
42 | _output = output;
43 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
44 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
45 | }
46 |
47 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
48 | {
49 |
50 | var specString = $$"""
51 | akka.test.single-expect-default = 3s
52 | akka.persistence {
53 | publish-plugin-commands = on
54 | journal {
55 | plugin = "akka.persistence.journal.mongodb"
56 | mongodb {
57 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
58 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
59 | use-write-transaction = {{(transaction ? "on" : "off")}}
60 | auto-initialize = on
61 | collection = "EventJournal"
62 | }
63 | }
64 | query {
65 | mongodb {
66 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
67 | refresh-interval = 1s
68 | }
69 | }
70 | }
71 | """;
72 |
73 | return ConfigurationFactory.ParseString(specString);
74 | }
75 |
76 | }
77 |
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbEventsByTagSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 | using Akka.Persistence.MongoDb.Query;
12 | using Akka.Persistence.Query;
13 | using Xunit.Abstractions;
14 | using Akka.Util.Internal;
15 | using System;
16 | using Akka.Actor;
17 |
18 | namespace Akka.Persistence.MongoDb.Tests
19 | {
20 | [Collection("MongoDbSpec")]
21 | public class MongoDbTransactionEventsByTagSpec : MongoDbEventsByTagSpecBase
22 | {
23 | public MongoDbTransactionEventsByTagSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, true)
24 | {
25 | }
26 | }
27 |
28 | [Collection("MongoDbSpec")]
29 | public class MongoDbEventsByTagSpec : MongoDbEventsByTagSpecBase
30 | {
31 | public MongoDbEventsByTagSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(output, databaseFixture, false)
32 | {
33 | }
34 | }
35 |
36 | public abstract class MongoDbEventsByTagSpecBase : Akka.Persistence.TCK.Query.EventsByTagSpec, IClassFixture
37 | {
38 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
39 | private readonly ITestOutputHelper _output;
40 |
41 | protected MongoDbEventsByTagSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
42 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbCurrentEventsByTagSpec", output)
43 | {
44 | _output = output;
45 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
46 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
47 |
48 | var x = Sys.ActorOf(TestActor.Props("x"));
49 | x.Tell("warm-up");
50 | ExpectMsg("warm-up-done", TimeSpan.FromSeconds(10));
51 | }
52 |
53 | protected override bool SupportsTagsInEventEnvelope => true;
54 |
55 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
56 | {
57 | var specString = $$"""
58 | akka.test.single-expect-default = 10s
59 | akka.persistence {
60 | publish-plugin-commands = on
61 | journal {
62 | plugin = "akka.persistence.journal.mongodb"
63 | mongodb {
64 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
65 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
66 | use-write-transaction = {{(transaction ? "on" : "off")}}
67 | auto-initialize = on
68 | collection = "EventJournal"
69 | event-adapters {
70 | color-tagger = "Akka.Persistence.TCK.Query.ColorFruitTagger, Akka.Persistence.TCK"
71 | }
72 | event-adapter-bindings = {
73 | "System.String" = color-tagger
74 | }
75 | }
76 | }
77 | snapshot-store {
78 | plugin = "akka.persistence.snapshot-store.mongodb"
79 | mongodb {
80 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
81 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
82 | use-write-transaction = {{(transaction ? "on" : "off")}}
83 | }
84 | }
85 | query {
86 | mongodb {
87 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
88 | refresh-interval = 1s
89 | }
90 | }
91 | }
92 | """;
93 |
94 | return ConfigurationFactory.ParseString(specString);
95 | }
96 |
97 | internal class TestActor : UntypedPersistentActor
98 | {
99 | public static Props Props(string persistenceId) => Actor.Props.Create(() => new TestActor(persistenceId));
100 |
101 | public sealed class DeleteCommand
102 | {
103 | public DeleteCommand(long toSequenceNr)
104 | {
105 | ToSequenceNr = toSequenceNr;
106 | }
107 |
108 | public long ToSequenceNr { get; }
109 | }
110 |
111 | public TestActor(string persistenceId)
112 | {
113 | PersistenceId = persistenceId;
114 | }
115 |
116 | public override string PersistenceId { get; }
117 |
118 | protected override void OnRecover(object message)
119 | {
120 | }
121 |
122 | protected override void OnCommand(object message)
123 | {
124 | switch (message) {
125 | case DeleteCommand delete:
126 | DeleteMessages(delete.ToSequenceNr);
127 | Sender.Tell($"{delete.ToSequenceNr}-deleted");
128 | break;
129 | case string cmd:
130 | var sender = Sender;
131 | Persist(cmd, e => sender.Tell($"{e}-done"));
132 | break;
133 | }
134 | }
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbJournalPerfSpec.cs:
--------------------------------------------------------------------------------
1 | #if !CICD
2 | using Akka.Configuration;
3 | using Akka.Persistence.TestKit.Performance;
4 | using Akka.Util.Internal;
5 | using System;
6 | using Xunit;
7 | using Xunit.Abstractions;
8 |
9 | namespace Akka.Persistence.MongoDb.Tests
10 | {
11 | [Collection("MongoDbSpec")]
12 | public class MongoDbJournalPerfSpec: JournalPerfSpec, IClassFixture
13 | {
14 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id)
15 | {
16 | // akka.test.single-expect-default = 10s
17 | var specString = @"
18 | akka.persistence {
19 | publish-plugin-commands = on
20 | journal {
21 | plugin = ""akka.persistence.journal.mongodb""
22 | mongodb {
23 | class = ""Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb""
24 | connection-string = """ + databaseFixture.MongoDbConnectionString(id) + @"""
25 | auto-initialize = on
26 | collection = ""EventJournal""
27 | }
28 | }
29 | }";
30 |
31 | return ConfigurationFactory.ParseString(specString)
32 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
33 | }
34 |
35 | public static readonly AtomicCounter Counter = new AtomicCounter(0);
36 |
37 | public MongoDbJournalPerfSpec(ITestOutputHelper output, DatabaseFixture databaseFixture) : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement()), "MongoDbJournalPerfSpec", output)
38 | {
39 | EventsCount = 1000;
40 | ExpectDuration = TimeSpan.FromMinutes(10);
41 | MeasurementIterations = 1;
42 | }
43 | }
44 | }
45 | #endif
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbJournalSetupSpec.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using System.Linq;
5 | using Akka.Actor;
6 | using Akka.Actor.Setup;
7 | using Akka.Configuration;
8 | using Akka.Persistence.TCK;
9 | using Akka.Persistence.TCK.Journal;
10 | using Akka.Persistence.TCK.Serialization;
11 | using Akka.TestKit;
12 | using MongoDB.Driver;
13 | using MongoDB.Driver.Linq;
14 | using Xunit;
15 | using Xunit.Abstractions;
16 |
17 | namespace Akka.Persistence.MongoDb.Tests
18 | {
19 | [Collection("MongoDbSpec")]
20 | public class MongoDbJournalSetupSpec : JournalSpec, IClassFixture
21 | {
22 | // TEST: MongoDb journal plugin set using Setup should behave exactly like when it is
23 | // set up using connection string.
24 | public MongoDbJournalSetupSpec(
25 | DatabaseFixture databaseFixture,
26 | ITestOutputHelper output)
27 | : base(CreateBootstrapSetup(databaseFixture), nameof(MongoDbSnapshotStoreSetupSpec), output)
28 | {
29 | Initialize();
30 | }
31 |
32 | private static ActorSystemSetup CreateBootstrapSetup(DatabaseFixture fixture)
33 | {
34 | var connectionString = new MongoUrl(fixture.ConnectionString);
35 | var clientSettings = MongoClientSettings.FromUrl(connectionString);
36 | var client = new MongoClient(clientSettings);
37 | var databaseName = connectionString.DatabaseName;
38 | var settings = client.Settings;
39 |
40 | return BootstrapSetup.Create()
41 | .WithConfig(CreateSpecConfig())
42 | .And(new MongoDbPersistenceSetup(null, null, databaseName, settings));
43 | }
44 |
45 | private static Config CreateSpecConfig()
46 | {
47 | var specString = @"
48 | akka.test.single-expect-default = 3s
49 | akka.persistence {
50 | publish-plugin-commands = on
51 | journal {
52 | plugin = ""akka.persistence.journal.mongodb""
53 | mongodb {
54 | class = ""Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb""
55 | auto-initialize = on
56 | collection = ""EventJournal""
57 | }
58 | }
59 | }";
60 |
61 | return ConfigurationFactory.ParseString(specString)
62 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbJournalSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 |
12 | namespace Akka.Persistence.MongoDb.Tests
13 | {
14 | [Collection("MongoDbSpec")]
15 | public class MongoDbTransactionJournalSpec : MongoDbJournalSpecBase
16 | {
17 | public MongoDbTransactionJournalSpec(DatabaseFixture databaseFixture) : base(databaseFixture, true)
18 | {
19 | }
20 | }
21 |
22 | [Collection("MongoDbSpec")]
23 | public class MongoDbJournalSpec : MongoDbJournalSpecBase
24 | {
25 | public MongoDbJournalSpec(DatabaseFixture databaseFixture) : base(databaseFixture, false)
26 | {
27 | }
28 | }
29 |
30 | public abstract class MongoDbJournalSpecBase : JournalSpec, IClassFixture
31 | {
32 | protected override bool SupportsRejectingNonSerializableObjects { get; } = false;
33 |
34 | protected MongoDbJournalSpecBase(DatabaseFixture databaseFixture, bool transaction)
35 | : base(CreateSpecConfig(databaseFixture, transaction), "MongoDbJournalSpec")
36 | {
37 | Initialize();
38 | }
39 |
40 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, bool transaction)
41 | {
42 | var specString = $$"""
43 | akka.test.single-expect-default = 3s
44 | akka.persistence {
45 | publish-plugin-commands = on
46 | journal {
47 | plugin = "akka.persistence.journal.mongodb"
48 | mongodb {
49 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
50 | connection-string = "{{databaseFixture.ConnectionString}}"
51 | use-write-transaction = {{(transaction ? "on" : "off")}}
52 | auto-initialize = on
53 | collection = "EventJournal"
54 | }
55 | }
56 | }
57 | """;
58 |
59 | return ConfigurationFactory.ParseString(specString)
60 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbLegacySerializationJournalSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2019 Lightbend Inc.
4 | // Copyright (C) 2013-2019 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Configuration;
9 | using Akka.Persistence.TCK.Journal;
10 | using Xunit;
11 |
12 | namespace Akka.Persistence.MongoDb.Tests
13 | {
14 | [Collection("MongoDbSpec")]
15 | public class MongoDbLegacySerializationJournalSpec : JournalSpec, IClassFixture
16 | {
17 | protected override bool SupportsRejectingNonSerializableObjects { get; } = false;
18 |
19 | protected override bool SupportsSerialization => false;
20 |
21 | public MongoDbLegacySerializationJournalSpec(DatabaseFixture databaseFixture) : base(CreateSpecConfig(databaseFixture), "MongoDbJournalSpec")
22 | {
23 | Initialize();
24 | }
25 |
26 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture)
27 | {
28 | var specString = @"
29 | akka.test.single-expect-default = 3s
30 | akka.persistence {
31 | publish-plugin-commands = on
32 | journal {
33 | plugin = ""akka.persistence.journal.mongodb""
34 | mongodb {
35 | class = ""Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb""
36 | connection-string = """ + databaseFixture.ConnectionString + @"""
37 | auto-initialize = on
38 | collection = ""EventJournal""
39 | legacy-serialization = on
40 | }
41 | }
42 | }";
43 |
44 | return ConfigurationFactory.ParseString(specString)
45 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbLegacySerializationSnapshotStoreSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Configuration;
9 | using Akka.Persistence.TCK.Snapshot;
10 | using Xunit;
11 |
12 | namespace Akka.Persistence.MongoDb.Tests
13 | {
14 | [Collection("MongoDbSpec")]
15 | public class MongoDbLegacySerializationSnapshotStoreSpec : SnapshotStoreSpec, IClassFixture
16 | {
17 | protected override bool SupportsSerialization => false;
18 |
19 | public MongoDbLegacySerializationSnapshotStoreSpec(DatabaseFixture databaseFixture) : base(CreateSpecConfig(databaseFixture), "MongoDbSnapshotStoreSpec")
20 | {
21 | Initialize();
22 | }
23 |
24 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture)
25 | {
26 | var specString = @"
27 | akka.test.single-expect-default = 3s
28 | akka.persistence {
29 | publish-plugin-commands = on
30 | snapshot-store {
31 | plugin = ""akka.persistence.snapshot-store.mongodb""
32 | mongodb {
33 | class = ""Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb""
34 | connection-string = """ + databaseFixture.ConnectionString + @"""
35 | auto-initialize = on
36 | collection = ""SnapshotStore""
37 | legacy-serialization = on
38 | }
39 | }
40 | }";
41 |
42 | return ConfigurationFactory.ParseString(specString)
43 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbPersistenceIdsSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Akka.Persistence.TCK.Journal;
9 | using Xunit;
10 | using Akka.Configuration;
11 | using Akka.Persistence.MongoDb.Query;
12 | using Akka.Persistence.Query;
13 | using Xunit.Abstractions;
14 | using Akka.Util.Internal;
15 | using System;
16 | using Akka.Actor;
17 | using Akka.Streams.TestKit;
18 | using System.Linq;
19 | using System.Diagnostics;
20 | using System.Threading.Tasks;
21 | using System.Reflection;
22 | using Reactive.Streams;
23 | using MongoDB.Driver.Core.Misc;
24 |
25 | namespace Akka.Persistence.MongoDb.Tests
26 | {
27 | [Collection("MongoDbSpec")]
28 | public class MongoDbTransactionPersistenceIdsSpec : MongoDbPersistenceIdsSpecBase
29 | {
30 | public MongoDbTransactionPersistenceIdsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
31 | : base(output, databaseFixture, true)
32 | {
33 | }
34 | }
35 |
36 | [Collection("MongoDbSpec")]
37 | public class MongoDbPersistenceIdsSpec : MongoDbPersistenceIdsSpecBase
38 | {
39 | public MongoDbPersistenceIdsSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
40 | : base(output, databaseFixture, false)
41 | {
42 | }
43 | }
44 |
45 | public abstract class MongoDbPersistenceIdsSpecBase : Akka.Persistence.TCK.Query.PersistenceIdsSpec, IClassFixture
46 | {
47 | private static readonly AtomicCounter Counter = new AtomicCounter(0);
48 |
49 | private readonly ITestOutputHelper _output;
50 |
51 | protected MongoDbPersistenceIdsSpecBase(ITestOutputHelper output, DatabaseFixture databaseFixture, bool transaction)
52 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement(), transaction), "MongoDbPersistenceIdsSpec", output)
53 | {
54 | _output = output;
55 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
56 | ReadJournal = Sys.ReadJournalFor(MongoDbReadJournal.Identifier);
57 | }
58 |
59 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id, bool transaction)
60 | {
61 | var specString = $$"""
62 | akka.test.single-expect-default = 3s
63 | akka.persistence {
64 | publish-plugin-commands = on
65 | journal {
66 | plugin = "akka.persistence.journal.mongodb"
67 | mongodb {
68 | class = "Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb"
69 | connection-string = "{{databaseFixture.MongoDbConnectionString(id)}}"
70 | use-write-transaction = {{(transaction ? "on" : "off")}}
71 | auto-initialize = on
72 | collection = "EventJournal"
73 | }
74 | }
75 | query {
76 | mongodb {
77 | class = "Akka.Persistence.MongoDb.Query.MongoDbReadJournalProvider, Akka.Persistence.MongoDb"
78 | refresh-interval = 1s
79 | }
80 | }
81 | }
82 | """;
83 |
84 | return ConfigurationFactory.ParseString(specString);
85 | }
86 |
87 | [Fact]
88 | public void ReadJournal_ConcurrentMessaging_should_work()
89 | {
90 | Enumerable.Range(1, 100).AsParallel().ForEach(_ => {
91 | Setup(Guid.NewGuid().ToString(), 1);
92 | Setup(Guid.NewGuid().ToString(), 1);
93 | });
94 | }
95 |
96 | private IActorRef Setup(string persistenceId, int n)
97 | {
98 | var sw = Stopwatch.StartNew();
99 | var pref = Sys.ActorOf(JournalTestActor.Props(persistenceId));
100 | for (int i = 1; i <= n; i++) {
101 | pref.Tell($"{persistenceId}-{i}");
102 | ExpectMsg($"{persistenceId}-{i}-done", TimeSpan.FromSeconds(10), $"{persistenceId}-{i}-done");
103 | }
104 | _output.WriteLine(sw.ElapsedMilliseconds.ToString());
105 | return pref;
106 | }
107 |
108 | public override void ReadJournal_AllPersistenceIds_should_find_new_events_after_demand_request()
109 | {
110 | var queries = ReadJournal.AsInstanceOf();
111 |
112 | Setup("h", 1);
113 | Setup("i", 1);
114 |
115 | var source = queries.PersistenceIds();
116 | var probe = source.RunWith(this.SinkProbe(), Materializer);
117 |
118 | probe.Within(TimeSpan.FromSeconds(10), () =>
119 | {
120 | probe.Request(1).ExpectNext();
121 | return probe.ExpectNoMsg(TimeSpan.FromMilliseconds(100));
122 | });
123 |
124 | Setup("j", 1);
125 | probe.Within(TimeSpan.FromSeconds(10), () =>
126 | {
127 | probe.Request(5).ExpectNext();
128 | return probe.ExpectNext();
129 | });
130 | }
131 | }
132 |
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbSettingsSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using FluentAssertions;
9 | using FluentAssertions.Extensions;
10 | using Xunit;
11 |
12 | namespace Akka.Persistence.MongoDb.Tests
13 | {
14 | [Collection("MongoDbSpec")]
15 | public class MongoDbSettingsSpec : Akka.TestKit.Xunit2.TestKit
16 | {
17 | [Fact]
18 | public void Mongo_JournalSettings_must_have_default_values()
19 | {
20 | var mongoPersistence = MongoDbPersistence.Get(Sys);
21 |
22 | mongoPersistence.JournalSettings.ConnectionString.Should().Be(string.Empty);
23 | mongoPersistence.JournalSettings.AutoInitialize.Should().BeTrue();
24 | mongoPersistence.JournalSettings.Collection.Should().Be("EventJournal");
25 | mongoPersistence.JournalSettings.MetadataCollection.Should().Be("Metadata");
26 | mongoPersistence.JournalSettings.LegacySerialization.Should().BeFalse();
27 | mongoPersistence.JournalSettings.CallTimeout.Should().Be(10.Seconds());
28 | }
29 |
30 | [Fact]
31 | public void Mongo_SnapshotStoreSettingsSettings_must_have_default_values()
32 | {
33 | var mongoPersistence = MongoDbPersistence.Get(Sys);
34 |
35 | mongoPersistence.SnapshotStoreSettings.ConnectionString.Should().Be(string.Empty);
36 | mongoPersistence.SnapshotStoreSettings.AutoInitialize.Should().BeTrue();
37 | mongoPersistence.SnapshotStoreSettings.Collection.Should().Be("SnapshotStore");
38 | mongoPersistence.SnapshotStoreSettings.LegacySerialization.Should().BeFalse();
39 | mongoPersistence.SnapshotStoreSettings.CallTimeout.Should().Be(10.Seconds());
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbSnapshotStoreSaveSnapshotSpec.cs:
--------------------------------------------------------------------------------
1 | using Akka.Configuration;
2 | using Akka.Persistence.TCK.Snapshot;
3 | using Xunit;
4 |
5 | namespace Akka.Persistence.MongoDb.Tests;
6 |
7 | [Collection("MongoDbSpec")]
8 | public class MongoDbSnapshotStoreSaveSnapshotSpec: SnapshotStoreSaveSnapshotSpec, IClassFixture
9 | {
10 | public MongoDbSnapshotStoreSaveSnapshotSpec(DatabaseFixture databaseFixture)
11 | : base(CreateSpecConfig(databaseFixture), "MongoDbSnapshotStoreSpec")
12 | {
13 | }
14 |
15 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture)
16 | {
17 | var specString = $$"""
18 | akka.test.single-expect-default = 3s
19 | akka.persistence {
20 | publish-plugin-commands = on
21 | snapshot-store {
22 | plugin = "akka.persistence.snapshot-store.mongodb"
23 | mongodb {
24 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
25 | connection-string = "{{databaseFixture.ConnectionString}}"
26 | use-write-transaction = on
27 | auto-initialize = on
28 | collection = "SnapshotStore"
29 | }
30 | }
31 | }
32 | """;
33 |
34 | return ConfigurationFactory.ParseString(specString);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbSnapshotStoreSetupSpec.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 | using Akka.Actor.Setup;
3 | using Akka.Configuration;
4 | using Akka.Persistence.TCK.Snapshot;
5 | using MongoDB.Driver;
6 | using Xunit;
7 | using Xunit.Abstractions;
8 |
9 | namespace Akka.Persistence.MongoDb.Tests
10 | {
11 | [Collection("MongoDbSpec")]
12 | public class MongoDbSnapshotStoreSetupSpec : SnapshotStoreSpec, IClassFixture
13 | {
14 | // TEST: MongoDb snapshot plugin set using Setup should behave exactly like when it is
15 | // set up using connection string.
16 | public MongoDbSnapshotStoreSetupSpec(
17 | DatabaseFixture databaseFixture,
18 | ITestOutputHelper output)
19 | : base(CreateBootstrapSetup(databaseFixture), nameof(MongoDbSnapshotStoreSetupSpec), output)
20 | {
21 | Initialize();
22 | }
23 |
24 | private static ActorSystemSetup CreateBootstrapSetup(DatabaseFixture fixture)
25 | {
26 | var connectionString = new MongoUrl(fixture.ConnectionString);
27 | var clientSettings = MongoClientSettings.FromUrl(connectionString);
28 | var client = new MongoClient(clientSettings);
29 | var databaseName = connectionString.DatabaseName;
30 | var settings = client.Settings;
31 |
32 | return BootstrapSetup.Create()
33 | .WithConfig(CreateSpecConfig())
34 | .And(new MongoDbPersistenceSetup(databaseName, settings, null, null));
35 | }
36 |
37 | private static Config CreateSpecConfig()
38 | {
39 | var specString = @"
40 | akka.test.single-expect-default = 3s
41 | akka.persistence {
42 | publish-plugin-commands = on
43 | snapshot-store {
44 | plugin = ""akka.persistence.snapshot-store.mongodb""
45 | mongodb {
46 | class = ""Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb""
47 | auto-initialize = on
48 | collection = ""SnapshotStore""
49 | }
50 | }
51 | }";
52 |
53 | return ConfigurationFactory.ParseString(specString);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/MongoDbSnapshotStoreSpec.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using Xunit;
9 | using Akka.Persistence.TCK.Snapshot;
10 | using Akka.Configuration;
11 |
12 | namespace Akka.Persistence.MongoDb.Tests
13 | {
14 | [Collection("MongoDbSpec")]
15 | public class MongoDbTransactionSnapshotStoreSpec : MongoDbSnapshotStoreSpecBase
16 | {
17 | public MongoDbTransactionSnapshotStoreSpec(DatabaseFixture databaseFixture) : base(databaseFixture, true)
18 | {
19 | }
20 | }
21 |
22 | [Collection("MongoDbSpec")]
23 | public class MongoDbSnapshotStoreSpec : MongoDbSnapshotStoreSpecBase
24 | {
25 | public MongoDbSnapshotStoreSpec(DatabaseFixture databaseFixture) : base(databaseFixture, false)
26 | {
27 | }
28 | }
29 |
30 | public abstract class MongoDbSnapshotStoreSpecBase : SnapshotStoreSpec, IClassFixture
31 | {
32 | protected MongoDbSnapshotStoreSpecBase(DatabaseFixture databaseFixture, bool transaction)
33 | : base(CreateSpecConfig(databaseFixture, transaction), "MongoDbSnapshotStoreSpec")
34 | {
35 | Initialize();
36 | }
37 |
38 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, bool transaction)
39 | {
40 | var specString = $$"""
41 | akka.test.single-expect-default = 3s
42 | akka.persistence {
43 | publish-plugin-commands = on
44 | snapshot-store {
45 | plugin = "akka.persistence.snapshot-store.mongodb"
46 | mongodb {
47 | class = "Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb"
48 | connection-string = "{{databaseFixture.ConnectionString}}"
49 | use-write-transaction = {{(transaction ? "on" : "off")}}
50 | auto-initialize = on
51 | collection = "SnapshotStore"
52 | }
53 | }
54 | }
55 | """;
56 |
57 | return ConfigurationFactory.ParseString(specString);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Serialization/MongoDbJournalSerializationSpec.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Akka.Configuration;
3 | using Akka.Persistence.TCK.Serialization;
4 | using Akka.Util.Internal;
5 | using Xunit;
6 | using Xunit.Abstractions;
7 |
8 | namespace Akka.Persistence.MongoDb.Tests.Serialization
9 | {
10 | [Collection("MongoDbSpec")]
11 | public class MongoDbJournalSerializationSpec : JournalSerializationSpec, IClassFixture
12 | {
13 | public static readonly AtomicCounter Counter = new AtomicCounter(0);
14 |
15 | private readonly ITestOutputHelper _output;
16 |
17 | public MongoDbJournalSerializationSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
18 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement()), nameof(MongoDbJournalSerializationSpec), output)
19 | {
20 | _output = output;
21 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
22 | }
23 |
24 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id)
25 | {
26 | var specString = @"
27 | akka.test.single-expect-default = 3s
28 | akka.persistence {
29 | publish-plugin-commands = on
30 | journal {
31 | plugin = ""akka.persistence.journal.mongodb""
32 | mongodb {
33 | class = ""Akka.Persistence.MongoDb.Journal.MongoDbJournal, Akka.Persistence.MongoDb""
34 | connection-string = """ + databaseFixture.MongoDbConnectionString(id) + @"""
35 | auto-initialize = on
36 | collection = ""EventJournal""
37 | }
38 | }
39 | }";
40 |
41 | return ConfigurationFactory.ParseString(specString)
42 | .WithFallback(MongoDbPersistence.DefaultConfiguration());
43 | }
44 |
45 | [Fact(Skip = "Waiting on better error messages")]
46 | public override void Journal_should_serialize_Persistent_with_EventAdapter_manifest()
47 | {
48 | base.Journal_should_serialize_Persistent_with_EventAdapter_manifest();
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb.Tests/Serialization/MongoDbSnapshotStoreSerializationSpec.cs:
--------------------------------------------------------------------------------
1 | using Akka.Configuration;
2 | using Akka.Persistence.TCK.Serialization;
3 | using Akka.Util.Internal;
4 | using Xunit;
5 | using Xunit.Abstractions;
6 |
7 | namespace Akka.Persistence.MongoDb.Tests.Serialization
8 | {
9 | [Collection("MongoDbSpec")]
10 | public class MongoDbSnapshotStoreSerializationSpec : SnapshotStoreSerializationSpec, IClassFixture
11 | {
12 | public static readonly AtomicCounter Counter = new AtomicCounter(0);
13 |
14 | private readonly ITestOutputHelper _output;
15 |
16 | public MongoDbSnapshotStoreSerializationSpec(ITestOutputHelper output, DatabaseFixture databaseFixture)
17 | : base(CreateSpecConfig(databaseFixture, Counter.GetAndIncrement()), nameof(MongoDbSnapshotStoreSerializationSpec), output)
18 | {
19 | _output = output;
20 | output.WriteLine(databaseFixture.MongoDbConnectionString(Counter.Current));
21 | }
22 |
23 | private static Config CreateSpecConfig(DatabaseFixture databaseFixture, int id)
24 | {
25 | var specString = @"
26 | akka.test.single-expect-default = 3s
27 | akka.persistence {
28 | publish-plugin-commands = on
29 | snapshot-store {
30 | plugin = ""akka.persistence.snapshot-store.mongodb""
31 | mongodb {
32 | class = ""Akka.Persistence.MongoDb.Snapshot.MongoDbSnapshotStore, Akka.Persistence.MongoDb""
33 | connection-string = """ + databaseFixture.MongoDbConnectionString(id) + @"""
34 | auto-initialize = on
35 | collection = ""SnapshotStore""
36 | }
37 | }
38 | }";
39 |
40 | return ConfigurationFactory.ParseString(specString);
41 | }
42 |
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb/Akka.Persistence.MongoDb.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(NetStandardLibVersion);$(NetFrameworkLibVersion)
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb/FullTypeNameDiscriminatorConvention.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 | using System;
8 | using System.Reflection;
9 | using MongoDB.Bson;
10 | using MongoDB.Bson.Serialization.Conventions;
11 |
12 | namespace Akka.Persistence.MongoDb
13 | {
14 | ///
15 | /// Our discriminator is the type's full name with the assembly name.
16 | /// Additional assembly information is excluded for forward compatibility.
17 | ///
18 | class FullTypeNameDiscriminatorConvention : StandardDiscriminatorConvention
19 | {
20 | public static readonly FullTypeNameDiscriminatorConvention Instance = new FullTypeNameDiscriminatorConvention("_t");
21 |
22 | public FullTypeNameDiscriminatorConvention(string element) : base(element) { }
23 |
24 | ///
25 | /// Our discriminator is the full type name with the assembly name.
26 | /// Additional assembly information is excluded for forward compatibility.
27 | ///
28 | ///
29 | ///
30 | /// full type name with the simple assembly name
31 | public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
32 | {
33 | var assemblyName = actualType.GetTypeInfo().Assembly.FullName.Split(',')[0];
34 | return $"{actualType.FullName}, {assemblyName}";
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Akka.Persistence.MongoDb/FullTypeNameObjectSerializer.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (C) 2009-2016 Lightbend Inc.
4 | // Copyright (C) 2013-2016 Akka.NET project
5 | //
6 | //-----------------------------------------------------------------------
7 |
8 | using System;
9 | using System.Reflection;
10 | using MongoDB.Bson;
11 | using MongoDB.Bson.Serialization;
12 | using MongoDB.Bson.Serialization.Conventions;
13 | using MongoDB.Bson.Serialization.Serializers;
14 |
15 | namespace Akka.Persistence.MongoDb
16 | {
17 | ///
18 | /// Represents a serializer for objects.
19 | ///
20 | internal class FullTypeNameObjectSerializer : ClassSerializerBase