-
-
Notifications
You must be signed in to change notification settings - Fork 223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
!!! FEATURE: Subscription Engine #5321
Conversation
# Conflicts: # Neos.ContentRepository.Core/Classes/ContentRepository.php
Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php
Outdated
Show resolved
Hide resolved
Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php
Outdated
Show resolved
Hide resolved
I had a quick look and id love to help with the wiring and sticking it all together :) We should probably really get a simple draft running of the catchups just getting their projection state first :) As that will require some work already |
…n they are registered for A catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections state is not safe as the other projection might not be behind - the order is undefined. This will make it possible to catchup projections from outside of the cr instance as proposed here: neos#5321
…n they are registered for A catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections state is not safe as the other projection might not be behind - the order is undefined. This will make it possible to catchup projections from outside of the cr instance as proposed here: neos#5321
Okay i thought further about our little content graph projection vs projection states vs event handlers dilemma and i think the solution is not necessary exposing just the in my eyes this object, being build by the content repository registry in some way, must reflect
that could look like: final readonly class ContentRepositoryGraphProjectionAndSubscribers
{
public function __construct(
public ContentGraphProjectionInterface $contentGraphProjection,
public Subscribers $subscribers, // must contain a subscriber for the $contentGraphProjection
public ProjectionStates $additionalProjectionStates, // must not contain the $contentGraphProjection state
) {
}
} or maybe a little more explicit so the factories dont have to deal with all the logic and we have control over the subscription ids: final readonly class ProjectionsAndCatchupHooksBetterVersionZwo
{
public function __construct(
public ContentGraphProjectionInterface $contentGraphProjection,
private Projections $additionalProjections,
private Subscribers $additionalSubscriber,
private array $catchUpHooksByProjectionClass
) {
}
public function getSubscribers(): Subscribers
{
$subscribers = iterator_to_array($this->additionalSubscriber);
$subscribers[] = new Subscriber(
SubscriptionId::fromString('contentGraphProjection'),
SubscriptionGroup::fromString('default'),
RunMode::FROM_BEGINNING,
new ProjectionEventHandler(
$this->contentGraphProjection,
$this->getCatchUpHooksForProjectionClass(ContentGraphProjectionInterface::class),
),
);
foreach ($this->additionalProjections as $projection) {
$subscribers[] = new Subscriber(
SubscriptionId::fromString(substr(strrchr($projection::class, '\\'), 1)),
SubscriptionGroup::fromString('default'),
RunMode::FROM_BEGINNING,
new ProjectionEventHandler(
$projection,
$this->getCatchUpHooksForProjectionClass($projection::class),
),
);
}
return Subscribers::fromArray($subscribers);
}
public function getAdditionalProjectionStates(): ProjectionStates
{
return ProjectionStates::fromArray(array_map(
fn ($projection) => $projection->getState(),
iterator_to_array($this->additionalProjections)
));
}
private function getCatchUpHooksForProjectionClass(string $projectionClass): ?CatchUpHookInterface
{
return $this->catchUpHooksByProjectionClass[$projectionClass] ?? null;
}
}
|
@mhsdesign thanks for your input! That's my current draft of the public function __construct(
private readonly ContentRepositoryId $contentRepositoryId,
EventStoreInterface $eventStore,
NodeTypeManager $nodeTypeManager,
ContentDimensionSourceInterface $contentDimensionSource,
Serializer $propertySerializer,
private readonly UserIdProviderInterface $userIdProvider,
private readonly ClockInterface $clock,
SubscriptionStoreInterface $subscriptionStore,
ContentGraphProjectionFactoryInterface $contentGraphProjectionFactory,
CatchUpHookFactoryInterface $contentGraphCatchUpHookFactory,
private readonly ContentRepositorySubscribersFactoryInterface $additionalSubscribersFactory,
) {
// ...
} |
As discussed that looks good ❤️ my idea had a flaw because i assumed the projection instance could be build by the registry which it CANNOT because we need factory dependencies.... and the thing with iterating over the event handlers to fetch their state via |
…n they are registered for A catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections state is not safe as the other projection might not be behind - the order is undefined. This will make it possible to catchup projections from outside of the cr instance as proposed here: neos/neos-development-collection#5321
…n they are registered for A catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections state is not safe as the other projection might not be behind - the order is undefined. This will make it possible to catchup projections from outside of the cr instance as proposed here: neos/neos-development-collection#5321
…n they are registered for A catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections state is not safe as the other projection might not be behind - the order is undefined. This will make it possible to catchup projections from outside of the cr instance as proposed here: neos/neos-development-collection#5321
Neos.ContentRepository.Core/Classes/Projection/CatchUpHook/CatchUpHookFactoryDependencies.php
Outdated
Show resolved
Hide resolved
# Conflicts: # Neos.ContentRepository.Core/Classes/ContentRepository.php # Neos.ContentRepository.Core/Classes/Factory/ContentRepositoryFactory.php # Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php # Neos.ContentRepositoryRegistry/Classes/Service/ProjectionService.php # Neos.ContentRepositoryRegistry/Classes/Service/ProjectionServiceFactory.php # phpstan-baseline.neon
…tchup-mechanism-3
without batching we had invalid state because the position is none (0) while the event is applied and persisted an the projection.
as we use SubscriptionStatus::from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Funny, the failing functional test shows just the exactly once problem I am still concerned about... I debugged into it locally, as the error output is not quite ideal. So what happens: We have two projections, one is "nice" and would just work, the other has a "saboteur" that throws right after the event was written to the projection DB. I have given this a good look now and I think we need to solve this question but otherwise seems ready to me. |
The concept of reactivation in case of errors was rather experimental. For core projections we would rather recommend a full replay, and its unlikely that other third party projections will profit from this now. Instead, if deemed necessary the reactivation api can be reintroduced later with little effort. Also, the case for detached projections is considered an edge-case and a simple replay should suffice. The change was done to minimalize confusion and simplify the api.
;) Alright that works I guess |
Ah yes thanks i forgot that i had to adjust this test as well. The tests shows what is probably very unlikely. Still as we dont rollback on ERROR now reactivation of a projection is more unlike to succeed than before. Instead - as we want to promote to replay for our core projections on errors, i removed my fancy Maybe we will fancy something like this as a debug tool later after all: Replay until X and then apply the next event again and again while debugging why it fails - but that is clearly an internal tool then and we dont need to confuse anyone yet with it ^^ |
🎉 Not quite sure I am in love with all this, but more so than the previous iteration of things. |
…to the subscription store ... which technically only coincidentally uses the same connection and dbal instance see neos/neos-development-collection#5321 (comment)
…to the subscription store ... which technically only coincidentally uses the same connection and dbal instance see neos/neos-development-collection#5321 (comment)
neos/neos-development-collection#5321 (comment) > Anyways, I think with the removed retry strategy we should just get rid of the automatic retry altogether right now. It's quite unlikely that a retry suddenly works without other changes. So I'd be fully OK if it was only possible to manually retry failed subscriptions for 9.0
…to the subscription store ... which technically only coincidentally uses the same connection and dbal instance see neos/neos-development-collection#5321 (comment)
see neos/neos-development-collection#5321 (comment) the save-point will only be used for REAL projection errors now and never rolled back if catchup errors occur. With that change in code the save-points are less important because a real projection error should better be thrown at the start before any statements, and even if some statements were issued and a full rollback is done its unlikely that a reactivateSubscription helps that case. Instead, to repair projections you should replay
neos/neos-development-collection#5321 (comment) > Anyways, I think with the removed retry strategy we should just get rid of the automatic retry altogether right now. It's quite unlikely that a retry suddenly works without other changes. So I'd be fully OK if it was only possible to manually retry failed subscriptions for 9.0
…to the subscription store ... which technically only coincidentally uses the same connection and dbal instance see neos/neos-development-collection#5321 (comment)
see neos/neos-development-collection#5321 (comment) the save-point will only be used for REAL projection errors now and never rolled back if catchup errors occur. With that change in code the save-points are less important because a real projection error should better be thrown at the start before any statements, and even if some statements were issued and a full rollback is done its unlikely that a reactivateSubscription helps that case. Instead, to repair projections you should replay
…to the subscription store ... which technically only coincidentally uses the same connection and dbal instance see neos/neos-development-collection#5321 (comment)
Related: #4746
Resolves: #4152
Resolves: #4908
migration to migrate the checkpoints to subscribers
TODO
SubscriptionEngine::reset()
(i.e. projection replay)Neos\ContentRepository\Core\Subscription
enough?)Optional: Allow to target specific groups/subscriptions via CLI commands(the SubscriptionEngine already supports this)use-> not possible asDateTimeImmutable::ATOM
for date formatting instead? But it clashes withdatetime_immutable
(useTypes::DATETIMETZ_IMMUTABLE
in schema)DATETIMETZ
is not supported by mysqlStatus->value
instead of name field!!!discoverNewSubscriptions
doesnt have a transaction?CatchupHadErrors
exception disturb the codeflow from dbal repos and doctrine orm things that are rolled back?Development
...getIteratorAggregate
for errors collection? Ensure errors are logged correctly by the throwable storage!--until
for debugging was removedProcessResult
as error? Should the cr throw?TODO pass the error subscription status to onAfterCatchUp, so that in case of an error it can be prevented that mails f.x. will be sent?CatchUpFailed
... but rather rename that and alsoSubscriptionEngineAlreadyProcessingException
toCatchUpError
and then have aErrorDuringCatchUp
?rename booting to booted?-> we agree that theING
is not a state, as it needs to be rather was.. but we dont have a better name