-
Notifications
You must be signed in to change notification settings - Fork 3
fix(api): fix transaction scope in EventsSubscription #360 #364
fix(api): fix transaction scope in EventsSubscription #360 #364
Conversation
…n_scope_in_EventsSubscription
Add PoC PrismaTransactionManager
This pull request is being automatically deployed with Vercel (learn more). coderscamp-website – ./🔍 Inspect: https://vercel.com/coderscamp/coderscamp-website/JBno7CAxEpDkdwusPyDPPPXuDR4h coderscamp-storybook – ./🔍 Inspect: https://vercel.com/coderscamp/coderscamp-storybook/2PHPBdoxKg4rZ58thbHYxfRrtC3B coderscamp-docs – ./🔍 Inspect: https://vercel.com/coderscamp/coderscamp-docs/3vaQSfjrbeAzKjZpnvmsjzrXt1Xv |
Codecov Report
@@ Coverage Diff @@
## main #364 +/- ##
==========================================
- Coverage 86.81% 86.13% -0.68%
==========================================
Files 125 128 +3
Lines 1456 1558 +102
Branches 191 207 +16
==========================================
+ Hits 1264 1342 +78
- Misses 192 216 +24
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
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.
Whereas there are very smart solutions, and I learned something (like injecting transaction context - how would you do something like that in C# (reflection / aspects)? Looks like a JS hack.
I think I've made a design mistake before, and it's dangerous to follow this approach.
Expansion of transaction context is probably not a way to go.
With that, our solution is not scalable and slices logic is coupled with another slices.
So, I propose to make something easier and more scalable.
Like in every messaging system, we need to handle message duplications instead of introducing transactional consistency.
Let's assume for a while, that source for subscriptions is not the database where we're storing progress, but some message broker etc. In this case, it's impossible to have transaction with covers also the side effect.
So I propose to leave subscriptions without transaction (add it only to EventStore) and accept possible duplicates.
What do you think?
We can try to mitigate duplicates with some buffer of processed commands?.
|
||
private internalState: PrismaTransactionManagerState = 'created'; | ||
|
||
public get state() { |
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.
Is it necessary? I don't see any usages of that getter.
} | ||
} | ||
|
||
private injectContextIntoCommand(command: ApplicationCommand) { |
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.
Looks like hacks simple to introduce only in JS :D
} | ||
|
||
executeCommand< | ||
TApplicationCommand extends ApplicationCommand<string, Record<string, unknown>, DefaultCommandMetadata>, |
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.
You can remove those generic parameters - there are just default values.
|
||
type PrismaTransactionManagerState = 'created' | 'executing' | 'executed'; | ||
|
||
class PrismaTransactionManager implements PrismaTransactionContext { |
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.
I'm wondering how is it better (beside injecting context into command) than interactive transactions from Prisma preview?
Is it not the same implemented differently, and only we need to maintain it on our own?
|
||
const eventsToPublishIds = this.transactionEventRepository.write(streamName, eventsToStore, streamVersion, context); | ||
|
||
return context.executeAfterTransaction(async (prisma) => { |
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.
Do we need callback here? What about await for transaction?
const eventsToPublishIds = this.transactionEventRepository.write(streamName, eventsToStore, streamVersion, context); | ||
|
||
return context.executeAfterTransaction(async (prisma) => { | ||
const eventsToPublish = await this.transactionEventRepository.readAll(eventsToPublishIds, prisma); |
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.
As far as I understand, we are after transaction here, so we dont need readAll from transactionEventRepository. Am I reight?
In any OOP language, you can use a decorator pattern or inheritance e.q. class ApplicationCommandWithContext extends ApplicationCommand {
constructor(readonly context: PrismaTransactionContext, ...restOfParams) {
super(...restOfParams);
}
}
// and then
if (command instanceof ApplicationCommandWithContext) {
} I use JS hack because it's simpler and faster to code.
I see some pros and cons of using the current implementation of transactional consistency:
I agree with you that we should abandon transactional consistency. In the end, it's harder to ensures and code.
As I understand, we have 3 cases when
I think that we can mitigate duplicates 'globally' in the 1st and 3rd cases:
|
Created issue for further development: |
Closes #360