Skip to content

Add injectable IScenarioStateStore for distributed scenario state#1430

Merged
StefH merged 5 commits intowiremock:masterfrom
m4tchl0ck:injectable-scenario-state-store
Mar 25, 2026
Merged

Add injectable IScenarioStateStore for distributed scenario state#1430
StefH merged 5 commits intowiremock:masterfrom
m4tchl0ck:injectable-scenario-state-store

Conversation

@m4tchl0ck
Copy link
Copy Markdown
Contributor

@m4tchl0ck m4tchl0ck commented Mar 10, 2026

Summary

  • Introduce IScenarioStateStore abstraction so users can inject a distributed implementation (Redis, SQL, etc.) to share scenario state across multiple WireMock.Net instances
  • Add InMemoryScenarioStateStore as the default implementation, preserving exact current behavior (backed by ConcurrentDictionary with OrdinalIgnoreCase)
  • Add FileBasedScenarioStateStore for persistent scenario state across restarts — in-memory cache backed by JSON file persistence in __admin/scenarios/
  • Move ScenarioState to WireMock.Net.Abstractions so the interface can reference it
  • Add ScenarioStateStore property to WireMockServerSettings for injection (following the IFileSystemHandler pattern)
  • Replace all direct ConcurrentDictionary<string, ScenarioState> usage in middleware, matcher, and server with IScenarioStateStore
  • Use TryGet pattern (bool TryGet(string, out ScenarioState?)) instead of nullable Get for safer state lookups

FileBasedScenarioStateStore

Reads come from an in-memory ConcurrentDictionary; all mutations (TryAdd, Update, AddOrUpdate, TryRemove, Clear) write through to disk as indented JSON files. The constructor loads existing state from disk on startup, enabling persistence across restarts and visibility into state via the filesystem.

var server = WireMockServer.Start(new WireMockServerSettings
{
    ScenarioStateStore = new FileBasedScenarioStateStore("/path/to/storage")
});

Test plan

  • All 15 InMemoryScenarioStateStoreTests pass (TryAdd, TryGet, GetAll, Update, AddOrUpdate, TryRemove, Clear, case-insensitivity)
  • All 20 FileBasedScenarioStateStoreTests pass (15 mirrored behavior tests + 5 file-persistence tests: disk write, disk delete, clear files, constructor loading, update persistence)
  • All 13 existing StatefulBehaviorTests pass unchanged — backward compatibility confirmed
  • All 4 additional Scenario-related tests pass
  • Full build succeeds with 0 errors across all target frameworks

@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch 3 times, most recently from 31d252c to 0d0881a Compare March 10, 2026 23:19
@StefH StefH added the feature label Mar 11, 2026
@StefH
Copy link
Copy Markdown
Collaborator

StefH commented Mar 11, 2026

@m4tchl0ck
Thank you for this.

However I'm working on a 2.0 version which has a lot of changes, so I will complete that one first...


About the CI fix:
I did also encounter some issues in my development on version 2, and this is the change:
https://github.com/wiremock/WireMock.Net/pull/1359/changes#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03f

So maybe revert the CI changes for this PR

@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch from a7f78fb to b304b90 Compare March 11, 2026 10:37
@m4tchl0ck
Copy link
Copy Markdown
Contributor Author

@m4tchl0ck Thank you for this.

However I'm working on a 2.0 version which has a lot of changes, so I will complete that one first...

About the CI fix: I did also encounter some issues in my development on version 2, and this is the change: https://github.com/wiremock/WireMock.Net/pull/1359/changes#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03f

So maybe revert the CI changes for this PR

Hi @StefH,
I changed my approach based on your approach in the mentioned commit.

If you are working on version 2.0 and would like to finish it before merging my feature, maybe you have an easy way to release a preview package with my changes?
Then I could try to use the sharing scenarios state between instances in my environment already.

@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch from b304b90 to 0d0881a Compare March 11, 2026 10:53
@m4tchl0ck
Copy link
Copy Markdown
Contributor Author

@m4tchl0ck Thank you for this.
However I'm working on a 2.0 version which has a lot of changes, so I will complete that one first...
About the CI fix: I did also encounter some issues in my development on version 2, and this is the change: https://github.com/wiremock/WireMock.Net/pull/1359/changes#diff-b803fcb7f17ed9235f1e5cb1fcd2f5d3b2838429d4368ae4c57ce4436577f03f
So maybe revert the CI changes for this PR

Hi @StefH, I changed my approach based on your approach in the mentioned commit.

If you are working on version 2.0 and would like to finish it before merging my feature, maybe you have an easy way to release a preview package with my changes? Then I could try to use the sharing scenarios state between instances in my environment already.

Ok, I reverted my change about CI as it doesn't work

@StefH
Copy link
Copy Markdown
Collaborator

StefH commented Mar 11, 2026

Would it also make sense to create a default implementation for file-based IScenarioStateStore ?

@m4tchl0ck
Copy link
Copy Markdown
Contributor Author

Would it also make sense to create a default implementation for file-based IScenarioStateStore ?

Yes, I agree. It was not my goal... yet.
I need to check a few things, and I will propose the solution.
We could also deliver in next PR :)

@StefH
Copy link
Copy Markdown
Collaborator

StefH commented Mar 11, 2026

Can be done in next PR sure.

@StefH
Copy link
Copy Markdown
Collaborator

StefH commented Mar 11, 2026

@m4tchl0ck

Version 2 is merged to master, so you probably need to resolve merge conflicts.

@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch 4 times, most recently from 180e1fb to 42f9513 Compare March 12, 2026 06:53
@m4tchl0ck
Copy link
Copy Markdown
Contributor Author

m4tchl0ck commented Mar 12, 2026

@StefH

Would it also make sense to create a default implementation for file-based IScenarioStateStore ?

I added FileBasedScenarioStateStore

@StefH
Copy link
Copy Markdown
Collaborator

StefH commented Mar 14, 2026

@m4tchl0ck
Can you fix merge conflict?

@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch from 42f9513 to 54dd45c Compare March 14, 2026 16:26
@m4tchl0ck
Copy link
Copy Markdown
Contributor Author

@m4tchl0ck Can you fix merge conflict?

done

options.FileSystemHandler = settings.FileSystemHandler;
options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
options.Logger = settings.Logger;
options.Scenarios = settings.ScenarioStateStore ?? options.Scenarios;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be

options.ScenarioStateStore = setings.ScenarioStateStore ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the ?? is not needed. No need to take options.Scenarios ?

And also rename options.Scenarios also to options.ScenarioStateStore ?

Copy link
Copy Markdown
Contributor Author

@m4tchl0ck m4tchl0ck Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ScenarioStateStore is nullable in WireMockServerSettings

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Renamed to options.ScenarioStateStore. The ?? is kept because settings.ScenarioStateStore is nullable.

ScenarioState is moved to the Abstractions project so it can be referenced
by the new IScenarioStateStore interface. The interface defines the contract
for storing and retrieving scenario states, enabling distributed implementations.
Wraps ConcurrentDictionary with OrdinalIgnoreCase comparer, preserving
exact current behavior. The Update method encapsulates read-modify-write
so distributed implementations can make it atomic.
…mers

Replace direct ConcurrentDictionary<string, ScenarioState> usage with
IScenarioStateStore across all consumer files. The store is injectable
via WireMockServerSettings.ScenarioStateStore, defaulting to the
InMemoryScenarioStateStore for backward compatibility.
In-memory ConcurrentDictionary backed by JSON file persistence in
__admin/scenarios/. Reads from cache, mutations write through to disk.
Constructor loads existing state from disk on startup.
@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch from 54dd45c to 37652f8 Compare March 16, 2026 11:10
@m4tchl0ck
Copy link
Copy Markdown
Contributor Author

Review comments addressed:

  • Changed Get to TryGet / TryGetValue pattern in IScenarioStateStore and both implementations
  • Renamed Scenarios to ScenarioStateStore in IWireMockMiddlewareOptions
  • Fixed WireMockMiddlewareOptionsHelper assignment to options.ScenarioStateStore = settings.ScenarioStateStore

@m4tchl0ck m4tchl0ck requested a review from StefH March 16, 2026 11:57
@m4tchl0ck m4tchl0ck requested a review from StefH March 17, 2026 07:33
…teStore

Move InMemoryScenarioStateStore from WireMock.Net.Minimal to
WireMock.Net.Shared so it lives alongside WireMockServerSettings.
This allows WireMockServerSettings.ScenarioStateStore to be
non-nullable with a default value, following the same pattern as
DefaultJsonSerializer. The null-coalescing fallback in
WireMockMiddlewareOptionsHelper is no longer needed.
@m4tchl0ck m4tchl0ck force-pushed the injectable-scenario-state-store branch from 8a2f593 to 2e3f52c Compare March 23, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants