SeqNo is not properly initialized on topic writer creation
Here is the PR with demo code that I hope will help easily replicate the issue.
In the README there is a code to test the serverless producer function. It makes 4 consecutive call to the function and in the output prints the sequenceNumber it gets from the writer. In all 4 call I got 1. If I check the stream in the data base, I see only first message.
I have tried writer and writer2 implementations. They both have same issue.
Additional info
Code snippet
await driver.ready();
// Create topic writer using the documented API
await using writer = createTopicWriter(driver, {
topic: ydsTopicPath,
producer: "producer-ts",
});
// Write message to topic
const messageData = JSON.stringify(message);
const seqNo = writer.write(
new TextEncoder().encode(messageData)
);
await writer.flush();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "success",
message: "Message sent to YDS topic",
data: message,
sequenceNumber: seqNo.toString(),
}),
};
The test code:
# Populate PRODUCER_URL from terraform outputs (use -raw to avoid quotes)
export PRODUCER_URL=$(terraform output -raw producer_function_url)
echo "Producer URL set to: $PRODUCER_URL"
# Send login event
echo "Sending login event for user123..."
curl -sS -X POST "$PRODUCER_URL" \
-H "Content-Type: application/json" \
-d '{"message": "User logged in", "user_id": "user123", "action": "login"}' \
| jq . || echo "Non-JSON or empty response"
# Send purchase event
echo "Sending purchase event for user123..."
curl -sS -X POST "$PRODUCER_URL" \
-H "Content-Type: application/json" \
-d '{"message": "Purchased item XYZ", "user_id": "user123", "action": "purchase"}' \
| jq . || echo "Non-JSON or empty response"
# Send view event
echo "Sending view event for user456..."
curl -sS -X POST "$PRODUCER_URL" \
-H "Content-Type: application/json" \
-d '{"message": "Viewed product ABC", "user_id": "user456", "action": "view"}' \
| jq . || echo "Non-JSON or empty response"
# Send logout event
echo "Sending logout event for user123..."
curl -sS -X POST "$PRODUCER_URL" \
-H "Content-Type: application/json" \
-d '{"message": "User logged out", "user_id": "user123", "action": "logout"}' \
| jq . || echo "Non-JSON or empty response"
The output:
Sending login event for user123...
{
"status": "success",
"message": "Message sent to YDS topic",
"data": {
"message": "User logged in",
"user_id": "user123",
"action": "login",
"timestamp": "2025-11-02T13:42:11.433Z"
},
"sequenceNumber": "1"
}
Sending purchase event for user123...
{
"status": "success",
"message": "Message sent to YDS topic",
"data": {
"message": "Purchased item XYZ",
"user_id": "user123",
"action": "purchase",
"timestamp": "2025-11-02T13:42:12.968Z"
},
"sequenceNumber": "1"
}
Sending view event for user456...
{
"status": "success",
"message": "Message sent to YDS topic",
"data": {
"message": "Viewed product ABC",
"user_id": "user456",
"action": "view",
"timestamp": "2025-11-02T13:42:13.500Z"
},
"sequenceNumber": "1"
}
Sending logout event for user123...
{
"status": "success",
"message": "Message sent to YDS topic",
"data": {
"message": "User logged out",
"user_id": "user123",
"action": "logout",
"timestamp": "2025-11-02T13:42:13.955Z"
},
"sequenceNumber": "1"
}
@nikolaymatrosov I think I've found a logical inconsistency.
How the sdk gets a seqno
this.#actor.on('writer.session', (event) => {
this.#seqNoManager.initialize(event.lastSeqNo)
})
How it uses
let seqNo = this.#seqNoManager.getNext(extra?.seqNo)
I think I've found a logical inconsistency. the problem is that when you start writing, the session has not been created yet, and when you call write, it gets 0.
I have a solution, create a promise like .ready() which will be resolved when the session is ready. But I don't want to add unnecessary methods, I'll think about what else I can do.
At the moment, the simplest solution is to add a small delay before write.
Creating a topic writer is already an async operation — could we make it resolve only after the session is fully initialized?
Creating a topic writer is already an async operation
No, it's not.
UPD: My bad. I finally read the doc.
Its syntax may be somewhat confusing, because the await does not have an awaiting effect when the variable is first declared, but only when the variable goes out of scope.
So why do I then have to use await in this statement?
await using writer = createTopicWriter(driver, {
topic: ydsTopicPath,
producer: "producer-ts",
});
I took it from package readme.
So why do I then have to use
awaitin this statement?
Simple way for making cleanup via Async Disposable - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncDispose https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/await_using
@nikolaymatrosov https://github.com/ydb-platform/ydb-js-sdk/pull/545 I'm still testing