Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions apps/server/src/orchestration-v2/EventSink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,20 @@ export type EventSinkV2Error = typeof EventSinkV2Error.Type;

export interface EventSinkV2Shape {
readonly write: (input: {
/** Return no events if another thread owns this native session at commit time. */
readonly unlessNativeThreadOwned?: Pick<
OrchestrationV2ProviderThread,
"driver" | "providerInstanceId" | "appThreadId" | "nativeThreadRef"
>;
readonly guardPendingUserInputCancellations?: boolean;
readonly commandId?: CommandId;
readonly events: ReadonlyArray<OrchestrationV2DomainEvent>;
}) => Effect.Effect<ReadonlyArray<OrchestrationV2StoredEvent>, EventSinkV2Error>;
readonly writeWithEffects: (input: {
readonly unlessNativeThreadOwned?: Pick<
OrchestrationV2ProviderThread,
"driver" | "providerInstanceId" | "appThreadId" | "nativeThreadRef"
>;
readonly guardPendingUserInputCancellations?: boolean;
readonly commandId?: CommandId;
readonly events: ReadonlyArray<OrchestrationV2DomainEvent>;
Expand Down Expand Up @@ -369,6 +378,20 @@ const layerBase: Layer.Layer<

return yield* commitThenPublish(
Effect.gen(function* () {
// Check ownership in the same transaction that claims the native session.
const imported = input.unlessNativeThreadOwned;
if (imported?.nativeThreadRef != null) {
const owners = yield* sql`
SELECT provider_thread_id
FROM orchestration_v2_projection_provider_threads
WHERE driver = ${imported.driver}
AND provider_instance_id = ${imported.providerInstanceId}
AND thread_id IS NOT ${imported.appThreadId}
AND json_extract(payload_json, '$.nativeThreadRef.nativeId') = ${imported.nativeThreadRef.nativeId}
LIMIT 1
`;
if (owners.length > 0) return [];
}
const normalized = yield* normalizeEvents(
input.guardPendingUserInputCancellations === true
? yield* guardUserInputCancellations(input.events)
Expand Down
Loading
Loading