Skip to content

Runtime plugin configuration for DurableState#3058

Open
ptrdom wants to merge 2 commits into
apache:mainfrom
ptrdom:durable-state-runtime-config
Open

Runtime plugin configuration for DurableState#3058
ptrdom wants to merge 2 commits into
apache:mainfrom
ptrdom:durable-state-runtime-config

Conversation

@ptrdom

@ptrdom ptrdom commented Jun 12, 2026

Copy link
Copy Markdown
Member

Motivation:
Like EventSourcedBehavior, DurableStateBehavior should allow configuring its persistence plugin at runtime so the same plugin class can back multiple, isolated stores (e.g. the pekko-persistence-r2dbc plugin).

Modification:
Add DurableStateBehavior.withDurableStateStorePluginConfig (Scala) and an overridable durableStateStorePluginConfig (Java), threaded through DurableStateBehaviorImpl, DurableStateSettings, and BehaviorSetup. Add Config-accepting overloads of DurableStateStoreRegistry.durableStateStoreFor and getDurableStateStoreFor that merge the runtime config ahead of the system config. The recovery-timeout is now resolved from the merged config so a plugin defined only at runtime resolves correctly.

Result:
DurableStateBehavior can be configured with a runtime plugin Config, and the store registry resolves the plugin using it.

Tests:

  • sbt persistence/mimaReportBinaryIssues persistence-typed/mimaReportBinaryIssues - success
  • sbt persistence-typed-tests/testOnly (all state.scaladsl and state.javadsl specs) - All tests passed

References:
Fixes #1798

Motivation:
Like EventSourcedBehavior, DurableStateBehavior should allow configuring its
persistence plugin at runtime so the same plugin class can back multiple,
isolated stores (e.g. the pekko-persistence-r2dbc plugin).

Modification:
Add DurableStateBehavior.withDurableStateStorePluginConfig (Scala) and an
overridable durableStateStorePluginConfig (Java), threaded through
DurableStateBehaviorImpl, DurableStateSettings, and BehaviorSetup. Add
Config-accepting overloads of DurableStateStoreRegistry.durableStateStoreFor
and getDurableStateStoreFor that merge the runtime config ahead of the system
config. The recovery-timeout is now resolved from the merged config so a plugin
defined only at runtime resolves correctly.

Result:
DurableStateBehavior can be configured with a runtime plugin Config, and the
store registry resolves the plugin using it.

Tests:
- sbt persistence/mimaReportBinaryIssues persistence-typed/mimaReportBinaryIssues - success
- sbt persistence-typed-tests/testOnly (all state.scaladsl and state.javadsl specs) - All tests passed

References:
Fixes apache#1798
@pjfanning pjfanning added this to the 2.0.0-M4 milestone Jun 12, 2026

@pjfanning pjfanning left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm

@He-Pin He-Pin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall the implementation is correct. The config merging logic properly leverages PersistencePlugin.createPlugin's internal readJournalPluginConfig.withFallback(systemConfig) + getConfig(configPath) pattern. A few minor improvement items below.

* configuration entry. The provided `pluginConfig` is used to configure the plugin at runtime, taking
* precedence over the plugin configuration defined in the actor system configuration.
*
* @since 2.0.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Parameter name shadows private method

The parameter pluginConfig: Config shadows the private method pluginConfig(pluginId: String) defined at line 76. While the current method body doesn't call the private method (so no compile error), this creates a maintenance trap: a future edit adding pluginConfig(someId) here would get a confusing "Config is not callable" error instead of invoking the private method.

Same concern applies to getDurableStateStoreFor at line 130.

Suggested fix — rename the parameter to avoid the collision:

final def durableStateStoreFor[T <: scaladsl.DurableStateStore[?]](pluginId: String, runtimeConfig: Config): T = {
  pluginFor(pluginIdOrDefault(pluginId, runtimeConfig), runtimeConfig).scaladslPlugin.asInstanceOf[T]
}

ConfigFactory.parseString(s"""
$store {
state.class = "${classOf[PersistenceTestKitDurableStateStoreProvider].getName}"
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🟡 Test only covers Scala API

This spec exercises DurableStateBehavior.withDurableStateStorePluginConfig (Scala DSL) and DurableStateStoreRegistry.durableStateStoreFor(pluginId, config) (Scala registry API).

The Java DSL counterpart (DurableStateBehavior.durableStateStorePluginConfig() returning Optional[Config]) and the Java registry API (getDurableStateStoreFor(clazz, pluginId, config)) are untested.

Per CONTRIBUTING.md: "Scala and Java DSL changes must keep API, docs, and tests in parity."

Suggested fix — add a Java DSL spec (e.g., RuntimeDurableStateStoreJavaSpec) that defines a DurableStateBehavior subclass overriding durableStateStorePluginConfig(), and verifies store isolation via getDurableStateStoreFor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runtime plugin configuration for DurableState

3 participants