|
1 | | -import type { StreamWriteMessage_FromClient, StreamWriteMessage_InitResponse, StreamWriteMessage_WriteRequest_MessageData } from "@ydbjs/api/topic"; |
2 | | -import type { CompressionCodec } from "../codec.js"; |
3 | | -import type { AsyncPriorityQueue } from "../queue.js"; |
4 | | -import type { TX } from "../tx.js"; |
5 | | -import { _flush } from "./_flush.js"; |
6 | | -import type { ThroughputSettings } from "./types.js"; |
| 1 | +import type { |
| 2 | + StreamWriteMessage_FromClient, |
| 3 | + StreamWriteMessage_InitResponse, |
| 4 | + StreamWriteMessage_WriteRequest_MessageData, |
| 5 | +} from '@ydbjs/api/topic' |
| 6 | +import type { CompressionCodec } from '../codec.js' |
| 7 | +import type { AsyncPriorityQueue } from '../queue.js' |
| 8 | +import type { TX } from '../tx.js' |
| 9 | +import { _flush } from './_flush.js' |
| 10 | +import type { ThroughputSettings } from './types.js' |
7 | 11 |
|
8 | | -export const _on_init_response = function on_init_response(ctx: { |
9 | | - readonly tx?: TX |
10 | | - readonly queue: AsyncPriorityQueue<StreamWriteMessage_FromClient>, |
11 | | - readonly codec: CompressionCodec, // Codec to use for compression |
12 | | - readonly buffer: StreamWriteMessage_WriteRequest_MessageData[]; // Array of messages in the buffer |
13 | | - readonly inflight: StreamWriteMessage_WriteRequest_MessageData[]; // Array of messages that are currently in-flight |
14 | | - readonly lastSeqNo?: bigint; // The last sequence number acknowledged by the server |
15 | | - readonly throughputSettings: ThroughputSettings; // Current throughput settings for the writer |
16 | | - updateLastSeqNo: (seqNo: bigint) => void; |
17 | | - updateBufferSize: (bytes: bigint) => void; // Function to update the buffer size |
18 | | -}, input: StreamWriteMessage_InitResponse) { |
19 | | - if (!ctx.lastSeqNo) { |
20 | | - // Store the last sequence number from the server. |
21 | | - ctx.updateLastSeqNo(input.lastSeqNo); |
22 | | - } |
| 12 | +export const _on_init_response = function on_init_response( |
| 13 | + ctx: { |
| 14 | + readonly tx?: TX |
| 15 | + readonly queue: AsyncPriorityQueue<StreamWriteMessage_FromClient> |
| 16 | + readonly codec: CompressionCodec // Codec to use for compression |
| 17 | + readonly buffer: StreamWriteMessage_WriteRequest_MessageData[] // Array of messages in the buffer |
| 18 | + readonly inflight: StreamWriteMessage_WriteRequest_MessageData[] // Array of messages that are currently in-flight |
| 19 | + readonly lastSeqNo?: bigint // The last sequence number acknowledged by the server |
| 20 | + readonly throughputSettings: ThroughputSettings // Current throughput settings for the writer |
| 21 | + readonly isSeqNoProvided?: boolean // Whether user provided seqNo (manual mode) |
| 22 | + updateLastSeqNo: (seqNo: bigint) => void |
| 23 | + updateBufferSize: (bytes: bigint) => void // Function to update the buffer size |
| 24 | + }, |
| 25 | + input: StreamWriteMessage_InitResponse |
| 26 | +) { |
| 27 | + let serverLastSeqNo = input.lastSeqNo || 0n |
| 28 | + let currentLastSeqNo = ctx.lastSeqNo |
| 29 | + let isFirstInit = currentLastSeqNo === undefined |
| 30 | + let lastSeqNoChanged = isFirstInit || currentLastSeqNo !== serverLastSeqNo |
23 | 31 |
|
| 32 | + // Return inflight messages to buffer |
24 | 33 | while (ctx.inflight.length > 0) { |
25 | | - const message = ctx.inflight.pop(); |
| 34 | + const message = ctx.inflight.pop() |
26 | 35 | if (!message) { |
27 | | - continue; |
| 36 | + continue |
28 | 37 | } |
29 | 38 |
|
30 | | - ctx.buffer.unshift(message); |
31 | | - ctx.updateBufferSize(BigInt(message.data.length)); |
| 39 | + ctx.buffer.unshift(message) |
| 40 | + ctx.updateBufferSize(BigInt(message.data.length)) |
| 41 | + } |
| 42 | + |
| 43 | + // If this is the first initialization or server provided a new lastSeqNo, and we're in auto seqNo mode, |
| 44 | + // renumber all messages in buffer to continue from serverLastSeqNo + 1 |
| 45 | + // Always renumber on first init, even if currentLastSeqNo === serverLastSeqNo (messages written before init) |
| 46 | + // Also renumber if there are messages in buffer that were written before init (their seqNo start from 1, not serverLastSeqNo + 1) |
| 47 | + let finalLastSeqNo = serverLastSeqNo |
| 48 | + let shouldRenumber = false |
| 49 | + // Only renumber in auto mode (when user didn't provide seqNo) |
| 50 | + if (!ctx.isSeqNoProvided && ctx.buffer.length > 0) { |
| 51 | + if (isFirstInit) { |
| 52 | + // First initialization: always renumber messages written before init |
| 53 | + shouldRenumber = true |
| 54 | + } else if (lastSeqNoChanged) { |
| 55 | + // Reconnection: renumber if server's lastSeqNo changed |
| 56 | + shouldRenumber = true |
| 57 | + } else if (ctx.buffer.length > 0) { |
| 58 | + // Check if messages in buffer were written before init (seqNo start from 1, not serverLastSeqNo + 1) |
| 59 | + // If first message's seqNo is <= serverLastSeqNo, it was written before init and needs renumbering |
| 60 | + let firstMessageSeqNo = ctx.buffer[0]?.seqNo |
| 61 | + if (firstMessageSeqNo !== undefined && firstMessageSeqNo <= serverLastSeqNo) { |
| 62 | + shouldRenumber = true |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + if (shouldRenumber) { |
| 68 | + let nextSeqNo = serverLastSeqNo + 1n |
| 69 | + // Renumber all messages in buffer sequentially starting from serverLastSeqNo + 1 |
| 70 | + for (let message of ctx.buffer) { |
| 71 | + message.seqNo = nextSeqNo |
| 72 | + nextSeqNo++ |
| 73 | + } |
| 74 | + // Update lastSeqNo to the last renumbered seqNo so flush() returns correct value |
| 75 | + finalLastSeqNo = nextSeqNo - 1n |
| 76 | + ctx.updateLastSeqNo(finalLastSeqNo) |
| 77 | + } else if (lastSeqNoChanged) { |
| 78 | + // Store the last sequence number from the server if we didn't renumber |
| 79 | + ctx.updateLastSeqNo(serverLastSeqNo) |
32 | 80 | } |
33 | 81 |
|
34 | | - _flush(ctx); // Flush the buffer to send any pending messages. |
| 82 | + // Flush the buffer to send any pending messages |
| 83 | + _flush(ctx) |
35 | 84 | } |
0 commit comments