Releases: SpineEventEngine/core-java
1.9.1
This is a minor release bringing a quality-of-life improvement, basing on Spine's real world usage.
EnvSetting
utility (#1550)
The EnvSetting
is a tool that allows configuring the value that may differ per environment type.
Previously it was an internal utility, but now it is available for all Spine-based projects.
Usage example:
EnvSetting<UserReader> setting = new EnvSetting<>();
setting.use(projectsUserReader(), Production.class)
.use(localUserReader(), Local.class)
.use(testsUserReader(), Tests.class);
// ...
// The instance is returned according to the current `Environment` type.
final UserReader reader = setting.value();
In addition to making this class public
, some changes were made to it comparing to its internal version:
- introduced method chaining for a more fluent and convenient API (as shown in the example);
- added a public endpoint to access the value configured for the current environment type;
- API is made thread-safe, allowing concurrent read operations while ensuring exclusive access for write operations to maintain data integrity.
Migration
This release is fully compatible with core-java
libraries 1.9.0. No special migration steps required.
1.9.0
This is part of Spine 1.9.0 release.
This update brings a number of API changes, and also addresses several known issues.
Breaking changes
The API of the ShardedWorkRegistry
has been changed.
In particular, a new PickUpOutcome pickUp(ShardIndex index, NodeId node)
method is introduced. Note, it returns an explicit result instead of Optional
, as previously. This outcome contains either of two:
ShardSessionRecord
— meaning that the shard is picked successfully,ShardAlreadyPickedUp
— a message that contains aWorkerID
of the worker who owns the session at the moment, and theTimestamp
when the shard was picked. This outcome means the session cannot be obtained as it's already picked.
Also, there is a new void release(ShardSessionRecord session)
method that releases the passed session.
Here is a summary of code changes for those using ShardedWorkRegistry
:
Before:
Optional<ShardProcessingSession> session = workRegistry.pickUp(index, currentNode);
if (session.isPresent()) { // Check if shard is picked.
// ...
session.get().complete(); // Release shard.
}
After:
PickUpOutcome outcome = workRegistry.pickUp(index, currentNode);
if (outcome.hasSession()) { // Check if shard is picked
// ...
workRegistry.release(outcome.getSession()); // Release shard.
}
Also, the new API allows getting the WorkerId
of the worker who owns the session in case if the shard is already picked by someone else and the Timestamp
when the shard was picked:
PickUpOutcome outcome = workRegistry.pickUp(index, currentNode);
if (outcome.hasAlreadyPickedBy()) {
WorkerId worker = outcome.getAlreadyPicked().getWorker();
Timestamp whenPicked = outcome.getAlreadyPicked().getWhenPicked();
// ...
}
Other changes
- Custom
Executor
forSystemSettings
(#1448).
Now, SystemSettings
allows customizing an Executor
to post the system events in parallel. This provides an opportunity to improve the control over the available CPU resources on a server instance.
var builder = BoundedContextBuilder.assumingTests();
var executor = ...;
builder.systemSettings()
.enableParallelPosting()
.useCustomExecutor(executor);
- Customization of gRPC
Server
viaGrpcContainer
(#1454).
It is now possible to access an underlying instance of Server
's builder when configuring the GrpcContainer
:
GrpcContainer.atPort(1654)
// `server` is an instance of `io.grpc.ServerBuilder`.
.withServer((server) -> server.maxInboundMessageSize(16_000_000))
// ...
.build();
This API is experimental and may change in future versions of Spine.
- Thorough copying of Bounded Contexts by
BlackBoxContext
's builder (#1495).
Previously, the BlackBoxContext
instances were built on top of BoundedContextBuilder
s by copying the internals of the latter builder. However, not all of the parts were copied properly.
This release improves the copying by including more pieces from the source BoundedContextBuilder
. In particular, all changes made to BoundedContextBuilder.systemSettings()
are now transferred as well.
- Custom handlers for failed delivery of a signal (#1496).
Now, the Delivery API allows to subscribe for any failures which occur during the reception of each signal. Additionally, end-users may now choose the way to handle the reception failures in terms of action in respect to the InboxMessage of interest.
Out-of-the-box, end-users are provided with two options:
- mark the
InboxMessage
as delivered — so that it does not block further delivery of messages; - repeat the dispatching of
InboxMessage
in a synchronous manner.
Alternatively, end-users may implement their own way of handling the reception failure.
The corresponding functionality is provided via the API of DeliveryMonitor
:
public final class MarkFailureDelivered extends DeliveryMonitor {
/**
* In case the reception of the {@code InboxMessage} failed,
* mark it as {@code DELIVERED} anyway.
*/
@Override
public FailedReception.Action onReceptionFailure(FailedReception reception) {
//// Error details are available as well:
// InboxMessage msg = reception.message();
// Error error = reception.error();
// notifyOf(msg, error);
return reception.markDelivered();
}
}
// ...
// Plugging the monitor into the Delivery:
DeliveryMonitor monitor = new MarkFailureDelivered();
Delivery delivery = Delivery.newBuilder()
.setMonitor(monitor)
// ...
.build();
ServerEnvironment
.when(MyEnvironment.class)
.use(delivery);
By default, InboxMessages
are marked as DELIVERED in case of failure of their reception.
- Prohibit calling
state()
from from@Apply
-ers (#1501).
It is now not possible to call Aggregate.state()
from @Apply
-ers. Previously, it was possible, but as discovered from real-world cases, such a functionality is prone to logical errors. End-users must use Aggregate.builder()
instead.
- Fix delivering signals to aggregate
Mirror
s in a multi-Bounded Context environment (#1502).
Previously, when several Bounded Contexts had their Aggregate
s "visible" (i.e. exposed via Mirror
), the delivery mechanism was confused with multiple Mirror
entity types which technically were distinct, but at the same time had exactly the same Type URL. Such a use-cases led to failures when Aggregate
state on read-side is updated by the framework code.
This release alters Type URLs, under which Mirror projections register themselves in Delivery
. The new type URL value includes the name of the Bounded Context — which makes this type URL invalid in terms of type discovery, but addresses the issue.
- Importing domain events from 3rd-party contexts properly in multi-tenant environments (#1503).
Previously, in a multi-tenant application, the imported events were dispatched in a straightforward manner, without specifying the TenantId
in the dispatching context. Now, this issue is resolved.
- Allow subscribers to receive a notification once an Entity stops matching the subscription criteria (#1504).
Starting this release, clients of gRPC Subscription API will start receiving updates once entities previously included into some subscription as matching, are modified and no longer pass the subscription criteria.
In particular, this will always be the case if an Entity becomes archived or deleted.
The new endpoint is available for Spine client under whenNoLongerMatching()
DSL, and is a part of Client's request API:
Client client = client();
client
/* ... */
.subscribeTo(Task.class)
.observe((task) -> { /* ... */ })
.whenNoLongerMatching(TaskId.class, (idOfNonMatchingEntity) -> { /* ... */})
.post();
- More granularity into
Shard
pick-up results (#1505).
In this release we start to distinguish the shard pick-up results. In particular, it is now possible to find out the reason of an unsuccessful shard pick-up. In particular, there may be some runtime issues, or a shard may already be picked-up by another worker.
Two new API endpoints were added to the DeliveryMonitor
to provide end-users with some control over such cases:
FailedPickUp.Action onShardAlreadyPicked(AlreadyPickedUp failure)
Invoked if the shared is already picked by another worker. The callback provides some insights into the pick-up failure, such as ID of the worker currently holding the shard, and Timestamp
of the moment when the shard was picked by it.
It is also required to return an action to take in relation to this case. By default, an action silently accepting this scenario is returned. End-users may implement their own means, e.g. retrying the pick-up attempt:
final class MyDeliveryMonitor extends DeliveryMonitor {
...
@Override
public FailedPickUp.Action onShardAlreadyPicked(AlreadyPickedUp failure) {
return failure.retry();
}
...
}
FailedPickUp.Action onShardPickUpFailure(RuntimeFailure failure)
This method is invoked if the shard could not be picked for some runtime technical reason. This method receives the ShardIndex
of the shard that could not be picked, and the instance of the occurred Exception
. It also requires to return an action to handle this case. By default, such failures are just rethrown as RuntimeException
s, but end-users may choose to retry the pick-up:
final class MyDeliveryMonitor extends DeliveryMonitor {
...
@Override
public FailedPickUp.Action onShardPickUpFailure(RuntimeFailure failure) {
return failure.retry();
}
...
}
- A build-in
Sample
type providing the generation of sample Proto messages was improved in relation to generation more humaneString
values (#1506).
1.8.2
This is a maintenance release of Spine core libraries. It extends the existing API, primarily focusing on adding new configuration knobs.
In particular, these changes were made since the last release:
-
#1443: Expand the
io.spine.server.Server
API to allow to:- obtain the Query, Subscription, and Command services after the
Server
is built; - add extra gRPC services to the same server.
- obtain the Query, Subscription, and Command services after the
-
#1448: Allow supplying
Executor
forSystemWriteSide
. -
#1454: Allow to configure the underlying gRPC server via
GrpcContainer
API.
No breaking changes to the existing API were made.
1.8.0
This release brings numerous fixes, updates to API and performance improvements.
Breaking Changes
-
Instead of
NodeId
,ShardSessionRecord
now usesWorkerId
to indicate who is currently processing which shard.Thus, all shard processing sessions should be completed before the migration.
Please see #1433 for details.
API Changes
-
Made
BlackBoxContext
implementCloseable
(as addition of #1402). -
BlackBoxContext
API has been extended to provide an instance ofClient
linked to the context under the test.It makes possible to use
Client
in tests, for example:BlackBoxContext context = BlackBoxContext.from(...); ClientRequest clientRequest = context.client().asGuest(); assertThat(clientRequest.select(ProjectView.class).run()) .hasSize(0); clientRequest.command(createProject()).postAndForget(); assertThat(clientRequest.select(ProjectView.class).run()) .hasSize(1);
Please note, that provided
Client
would inheritTenantId
fromBlackBoxContext
, but would NOT inheritUserId
andZoneId
.Check #1407 for details.
-
Made API calls for the conditional settings of
ServerEnvironment
"lazy".Previously, ServerEnvironment provided two kinds of API calls for the conditional settings:
ServerEnvironment.when(Staging.class) // This call is handy when `myStorageFactory` is already available. .use(myStorageFactory) ServerEnvironment.when(PreProduction.class) // And this one looks lazy, so that it is only executed when and _if_ requested. .use((env) -> createMySqlStorage())
However, in fact, there was no "lazy" behavior, which caused numerous workarounds to actually postpone the initialization of environment-specific settings until they start to make sense.
This release addresses the issue by making the behavior truly "lazy" (see #1421).
Fixes
-
Enabled
IntegrationBroker
dispatch events regardless of registration order of subscribing and publishing Bounded Contexts (see #1402). -
Transformation of an Entity's state during
Migration
has been changed so that thenewState
completely overwrites the old one within the migration transaction (see #1405). -
The internals of
IntegrationBroker
were made more thread-safe (see #1423). -
SubscriptionService
now properly locates the Bounded Context when subscribing to events produced by standalone producers, such as descendants ofAbstractCommandHandler
orAbstractEventReactor
(see #1423).
Performance
- Improved Catch-up caching (see #1406 for details).
1.7.6
This is another patch release of Spine 1.x. It addresses some issues and inconveniences operating the Projection catch-up.
- It is now not possible to start the catch-up for Aggregate's
MirrorProjection
— as such projections are built from system events only, and their catch-up makes not much sense. - The run-time of catch-up is made more stable, taking into account the experience of exploring this feature in production use cases.
1.7.5
This is a patch release of Spine 1.x. It follows up on the production use of Spine libraries and brings a few requested improvements.
- The integration with gRPC server has been improved for better results in dealing with heavy-duty subscriptions.
BlackBoxContext
API has been extended with a few endpoints to test for the positive outcomes.
Additionally, several improvements of config
scripts were brought to this version.
Compare v1.7.0 and v1.7.5.
1.7.0
This release brings numerous API improvements, as well as fixes and infrastructure updates to the framework.
Breaking Changes
-
The
BlackBoxContext
-based tests now fail if a runtime exception was thrown within the signal handlers.In order to address #1314, we've decided to enforce the fail-fast approach within the BBC tests done in #1322. From now on, if a test case had any runtime exceptions thrown from signal handlers the test is failed by the BBC. While it may be a breaking change for some, we believe it worth fixing such issues right away than hiding them under the carpet.
If one requires to fall back to the previous behavior, the BBC instance can be configured using the newly introduced
tolerateFailures
method. -
The
grand_origin
field is no longer set to a default instance for the signalOrigin
if no grand origin is present (see #1341 for details). -
The
ServerEnvironment
API is improved and changed as a result of the #1315 and related discussions in a series of PRs (#1327, #1331, #1336).The previously deprecated
configure...()
API is removed in favor of the new fluentwhen().use()
API.So now, instead of smth like this:
ServerEnvironment.use(productionStorageFactory, Production.class) .use(testingStorageFactory, Tests.class) .use(memoizingTracerFactory, Production.class)
One should use the following snippet:
ServerEnvironment.when(Production.class) .use(productionStorageFactory) .use(memoizingTracerFactory); ServerEnvironment.when(Tests.class) .use(testingStorageFactory);
API Changes
-
Change
type validation requirements are relaxed for primitive type changes.The
new_value
field is no longer a required one forStringChange
andBytesChange
types. See #1307 for details. -
Introduced simplified
unicast()
methods in theEventRouting
.The new
unicast()
API allows simplifying and prettifying the even routing for the cases where a singular ID is used.Now, instead of smth like this:
@OverridingMethodsMustInvokeSuper @Override protected void setupEventRouting(EventRouting<AirportId> routing) { super.setupEventRouting(routing); routing.route(FlightScheduled.class, (e, ctx)-> EventRoute.withId(e.getAirport())); }
One can use the following API:
@OverridingMethodsMustInvokeSuper @Override protected void setupEventRouting(EventRouting<AirportId> routing) { super.setupEventRouting(routing); routing.unicast(FlightScheduled.class, FlightScheduled::getAirport) }
To find out more details on the new API please check #1317.
-
Added
localDate
andlocalDateTime
helpers to theWithTime
interface.This feature allows accessing
localDate
andlocalDateTime
methods from within all the signals. From now on, if one requires a JavaLocalDate
orLocalDateTime
instances over the ProtobufTimestamp
or JavaInstant
that were previously available for signals, they may use the new API to simplify such a conversion and access to the signalstimestamp
field.See #1319 for additional details.
Fixes
-
Improved multitenant delivery support by ensuring the tenant is properly propagated within the delivery (see #1308).
-
Fixed a typo in the
io.spine.client.Client
shutdownTimeout
method (see #1339). -
Fixed dispatching of rejections caused by a particular command (see #1318 and #1343).
Infrastructure
- The libraries now do not use
implementation
for compile-only annotations likeerrorprone
annotations but use the newly introducedcompileOnlyApi
configuration for such dependencies (see #1340).
Compare v1.6.0 and v1.7.0.
1.6.0
This release brings numerous API improvements, as well as fixes and infrastructure updates to the framework.
API changes
Client
- The ability to
postAndForget()
a command is added to theClient
. This method should be called when the user does not care about events/rejections produced by a command.
The previously usedpost()
method is reserved for cases when one or more event types are actually observed by the client. The value returned bypost()
can no longer be ignored [#1292]. - Event subscriptions are now more flexible, allowing to subscribe to events produced by non-entity objects (e.g.
AbstractEventReactor
) as well as events not explicitly declared in anyBoundedContext
[#1258]. - The
Client
is extended with methods to handle streaming and server errors when executing requests [#1270].
Server
- The custom environments support is introduced [#1274, #1293].
TheEnvironment
now exposes API to register user-defined environment types and to determine which one is enabled at any given moment of time. See the release notes ofbase
.
TheServerEnvironment
allows to configure environment-dependent values, as follows:
StorageFactory factory = InMemoryStorageFactory.newInstance();
ServerEnvironment.instance()
.use(factory, Tests.class);
The Spine framework provides two environments out of the box: Production
and Tests
.
- Breaking change: Most of the
@Internal
methods ofBoundedContext
moved to its internal classInternalAccess
instance of which is available via theinternalAccess()
method.
The method is available only to the server-side framework code. - Delivery API is extended with a factory method which allows to create asynchronous version of local
Delivery
[#1265]. - The
Pair
can now be created from an already existingOptional
[#1296]. - The proper support to the
CommandBus
filters which throw rejections is added [#1295].
Model
- The
@External
annotation is introduced to mark the handler method parameters of an external origin. It replaces the previously used for this purpose(external = true)
attribute of@Subscribe
,@React
, and@Command
annotation. The attribute is deprecated [#1269]. (set_once)
constraint in entity states is no longer ignored [#1268].@ByField
is deprecated in favour of@Where
[#1270].
Logging
- The
DiagnosticLog
messages are made more detailed [#1262]. - The standard framework exceptions are expanded with more info [#1255].
Testing
Various quality-of-life changes are introduced for the testing API.
See #1249, #1251, #1252, and #1261 for details.
Some of the testing API changes are breaking. They include:
BlackBoxBoundedContext
is renamed toBlackBoxContext
.- Outdated
Verify
-based API is removed. BlackBoxContext
no longer exposeseventBus()
andcommandBus()
.- The
BlackBoxContext.subscribeTo(Topic)
semantics changed. See #1249. - Simplified combinations of
UserId
andZoneId
parameters ofBlackBoxContext
.
Fixes
The Migration
logic is fixed to properly support entity state updates [#1298].
Infrastructure
The project build scripts are migrated to Kotlin [#1278].
1.5.0
This major update of the library brings a number of new features and performance improvements.
- Projections now support an automated run-time catch-up (#1221).
- The client API was made less error-prone by enforcing the strong typing for the columns and properties of Entities and Events. The list of the model fields is generated as Java code by the Spine compiler at build-time (#1229, #1246).
- A data migration API is now available for
ProcessManager
s andProjection
s. It automates the process of updating the existing data upon the changes in the domain model (#1241). - The logging records made from within the Entity handlers now print the full signature of the called method (#1242).
- It is now possible to specify an actor when composing a test scenario with
BlackBoxBoundedContext
API (#1242). CommandBus
is no longer responsible for validating the first field of the transmittedCommand
s; instead, this is a job of theRepository
for the targetEntity
(#1245).
The API of InboxMessage
has been changed so that any of the existing InboxMessage
s become incompatible (#1239). Please make sure to deliver the messages from all of your production Inbox
es prior to updating to the new version.
Also, a number of minor improvements and issues were addressed. Please see the list of closed pull requests for more details.
1.4.0
This release concentrates mainly on performance improvements.
- The new generated validation API is now available with the beta status.
(pattern)
option now only applies to non-empty string values.- The bug preventing users from declaring enum-type columns is fixed.
- API of code libraries is extended.