Skip to content

[All] Correct SDK documentation and examples - #729

Merged
teodordelibasic-db merged 36 commits into
mainfrom
codex/audit-sdk-snippets
Aug 14, 2026
Merged

[All] Correct SDK documentation and examples#729
teodordelibasic-db merged 36 commits into
mainfrom
codex/audit-sdk-snippets

Conversation

@teodordelibasic-db

@teodordelibasic-db teodordelibasic-db commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

What changes are proposed in this pull request?

Correct README snippets, checked-in examples, and public API doc examples across Rust, Python, Java, Go, pure Go, TypeScript, C++, and .NET so they compile and demonstrate the current APIs. The changes fix API names and arguments, stream format selection, dependency setup, recovery and callback semantics, resource cleanup, and queue-then-flush ingestion. They also add the generated-message fixture needed to run the .NET Protobuf example.

The documentation had drifted as the public APIs evolved, leaving examples that either failed to compile or demonstrated incorrect recovery, durability, and throughput patterns. Keeping these examples aligned with the supported APIs prevents users from copying invalid or unnecessarily slow client code.

This PR documents current behavior. The following implementation issues are deferred:

  • Python _zerobus_core.pyi still has wrong get_unacked_records() return types, create_stream argument order, async signatures, and is missing row-stream get_unacked_batches().
  • Python stream_id and get_state() still return placeholder values (stream-placeholder-id and constant OPENED).
  • TypeScript custom header callbacks are invoked once and snapshotted; they do not refresh on recovery.
  • Java JNI logs AckCallback exceptions but does not clear the pending Java exception, which can poison later callback operations.
  • Java setRecoveryBackoffMs does not reject negative values.
  • Java NativeTestHelper remains a public production class despite exposing test-only JNI symbols.
  • Python ingest_record_nowait / ingest_records_nowait are documented as unsafe but do not emit a deprecation warning.
  • A flush timeout can leave a Pure-Go or C++ stream active, so unacked retrieval fails until the stream is actually closed. Recovering that case needs an implementation change.
  • go/version.go remains 1.4.0. The user-agent string bumps with the next Go release, not this docs PR.

How is this tested?

All runnable examples and executable documentation snippets were compiled or type-checked. Representative JSON, Protobuf, Arrow, batch, recovery, and custom-header paths were also exercised end to end.

The affected SDK formatters, linters, unit tests, example builds, and doc tests pass. The .NET suite passes 47 unit and 54 integration tests. TypeScript build, tests, and example type-check pass; its all-features Clippy check still reports three pre-existing warnings unrelated to these documentation changes. The C++ Arrow example was source-reviewed but not linked because Apache Arrow C++ was unavailable; the other C++ examples built and their JSON batch and custom-header paths were exercised.

@teodordelibasic-db teodordelibasic-db changed the title Correct SDK documentation and examples [All] Correct SDK documentation and examples Aug 13, 2026
@teodordelibasic-db
teodordelibasic-db marked this pull request as ready for review August 13, 2026 06:50
@teodordelibasic-db
teodordelibasic-db requested a review from a team August 13, 2026 07:23
@teodordelibasic-db teodordelibasic-db self-assigned this Aug 13, 2026
# ack = stream.ingest_record(record_dict) # Deprecated
# offset = ack.wait_for_ack() # Extra step needed

end_time = time.time()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ingest_record_nowait() and ingest_records_nowait() spawn detached tasks and discard enqueue errors. flush() can complete before those tasks allocate offsets, so this example may report durability while losing submissions.

@teodordelibasic-db @elenagaljak-db I think we should remove nowait from examples and put it on a deprecation path.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed nowait from the examples. Docs now rank ingest_records_offset() + one flush() as the bulk path and call nowait unsafe (detached tasks, not synchronized with flush). Putting the APIs on a real deprecation path (DeprecationWarning) is a follow-up, not this docs PR.

Comment thread python/README.md Outdated
unacked = stream.get_unacked_records() # Returns List[bytes]
stream.close()
except ZerobusException as e:
unacked = list(stream.get_unacked_records())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch also handles immediate enqueue errors that leave the stream active. Calling get_unacked_records() or recreate_stream() then fails and masks the original error.

Please separate enqueue failures from terminal failures, close before recovery, and protect the replacement stream with try/finally.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. The snippet now distinguishes that enqueue failures can leave the stream active, closes before get_unacked_records()/recreate_stream(), re-raises the original ingest error if unacked retrieval fails, and wraps the replacement stream in try/finally.

Comment thread python/zerobus/sdk/shared/config.py Outdated
when records are acknowledged by the server or encounter errors.
Subclass this in Python to create custom callbacks that are invoked once per
logical ingest submission. A batch submission produces one callback, not one
callback per record in the batch.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-queue validation, size, type, and closed-stream failures do not generate callbacks, so this does not apply to every attempted submission.
Please qualify it as one callback per successfully queued submission that later acknowledges or fails.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qualified: one callback per successfully queued logical ingest submission that later acknowledges or fails. Pre-queue validation / size / type / closed-stream failures raise immediately and do not generate a callback.

Comment thread python/README.md
Comment on lines 373 to -377
```python
unacked_batches = stream.get_unacked_batches() # Returns List[List[bytes]]

@nikolaobradovic-db nikolaobradovic-db Aug 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ranks per-record ingest_record_nowait() highest but omits the batch APIs. Batch ingestion amortizes the Python→Rust crossing and is the preferred hot path. Additionally, detached nowait submissions are not safely synchronized with flush.

Please include the batch APIs and recommend ingest_records_offset() plus one flush for reliable bulk ingestion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Table at 385

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Table now ranks ingest_records_offset() + one flush() first. nowait is listed as unsafe and is not recommended for bulk ingestion.

* );
* ```
*
* **How to use custom authentication (PAT, etc.):**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exported interface requires async getHeaders(): Promise<...>, while this example and createStream() require synchronous getHeadersCallback. A class implementing the public interface cannot be passed to the documented API.

Please expose and document one provider type matching createStream().

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exported HeadersProvider is now the shape createStream() actually accepts: getHeadersCallback returning header tuples synchronously. The docs no longer show an async getHeaders() class.

Comment thread typescript/src/lib.rs
///
/// * `stream` - The failed or closed stream to recreate
/// * `stream` - The terminally failed stream to recreate. The TypeScript wrapper
/// must not have been closed because `close()` releases its native handle.

@nikolaobradovic-db nikolaobradovic-db Aug 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLM find:
TypeScript custom credentials cannot refresh
Affected: typescript/src/lib.rs:1024-1046

The callback runs once during stream creation and its result is stored in StaticHeadersProvider. Long-running streams therefore reuse the original token during recovery, so rotating or expiring custom credentials eventually break reconnection.

Keep the threadsafe callback in a HeadersProvider adapter and invoke it whenever the Rust core requests fresh headers. Support invalidation if required by the core contract.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this is a real implementation bug. Out of scope for this docs PR. The headers-provider docs now say the callback is invoked once at stream creation and that token refresh is not wired through. Follow-up will keep the callback and invoke it whenever the core requests headers.

Comment thread typescript/README.md Outdated
// Optional: Inspect what needs recovery (must be called on closed stream)
// Optional: Inspect what needs recovery after a terminal stream failure.
const unackedBatches = await stream.getUnackedBatches();
console.log(`Batches to recover: ${unackedBatches.length}`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getUnackedBatches() runs before cleanup protection, and this catch can also handle non-terminal failures. An inspection failure can leak the original stream and mask the ingestion error.

Please use an outer finally, recover only from confirmed terminal failures, and close every replacement stream in a nested finally.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated: outer finally always closes the original stream, recovery only runs after a failure, and the replacement stream is closed in a nested finally.

Comment on lines +109 to +110
AirQuality.create({ deviceName: 'sensor-002', temp: 23, humidity: 67 }),
AirQuality.create({ deviceName: 'sensor-003', temp: 24, humidity: 69 })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typescript/examples/proto/batch.ts:113-116

Waiting here before queueing the next batch serializes the example into one server round trip per batch.

Please queue all demonstration batches first, then call flush() once or wait only on the final offset. The JSON batch example has the same issue.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proto and JSON batch examples now queue every demonstration batch, then flush() once.


### Code Highlights

**Offset-based API (Recommended):**

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typescript/examples/json/README.md:65-71

This first “Recommended” pattern immediately waits after one ingest. Although valid for strict low-volume confirmation, it is not the default pattern readers should copy.

Please show loop-then-flush() first and move this into a clearly labeled low-volume section. The Protobuf README has the same ordering issue.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON and Protobuf example READMEs now lead with loop-then-flush(). Immediate waitForOffset after a single ingest is labeled as a low-volume confirmation pattern, not the default.

Comment thread typescript/README.md

main().catch((error) => {
console.error('Fatal error:', error);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logs a fatal error but leaves the process exit status as zero, so copied CLI or CI code can report success after ingestion fails.

Please set process.exitCode = 1 or rethrow.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main().catch now sets process.exitCode = 1.

@nikolaobradovic-db

Copy link
Copy Markdown
Contributor

I don't see any changes to GO SDK while it was mentioned in PR desc. Maybe you audited it and there were no stale docs/examples?

>>>
>>> # New optimized API
>>> offset = stream.ingest_record_offset(b"data")
>>> offset = stream.ingest_record_offset('{"value": "data"}')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

python/zerobus/init.py:13

Callbacks represent logical submissions and can correspond to batches, so “Record acknowledged” is too specific.

Please use “Submission acknowledged” for consistency.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to “Submission acknowledged”.

@zlata-stefanovic-db
zlata-stefanovic-db self-requested a review August 14, 2026 10:59
@teodordelibasic-db
teodordelibasic-db requested a review from a team August 14, 2026 11:04
Comment thread dotnet/README.md Outdated

```bash
dotnet add package Databricks.Zerobus.Ingest.Sdk
dotnet add package Databricks.Zerobus --version 0.5.1

@elenagaljak-db elenagaljak-db Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 0.5.1? Databricks.Zerobus is incorrect

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The package id is Databricks.Zerobus.Ingest.Sdk. Left it unpinned under "NuGet (when published)" since it is not on NuGet yet.

Comment thread go/zerobus.go
Comment on lines +726 to 733
// IMPORTANT: Call this on a failed stream before Close(). Close() nils the
// handle and frees native resources, so a later GetUnackedRecords() call fails.
//
// Use this method to:
// - Retrieve unacknowledged records after stream failure for retry logic
// - Check which records weren't durably written after Close() fails
// - Implement custom retry strategies after stream errors
//

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The important note and the second bullet contradict

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. A flush timeout can leave the stream active, so that bullet was wrong. It now says to inspect queued-but-unacked payloads before Close(), matching the IMPORTANT note.

Comment thread python/NEXT_CHANGELOG.md Outdated
Comment on lines +16 to +17
- Documented that `get_unacked_records()` and `recreate_stream()` require a closed
stream, and that enqueue failures must be closed before recovery.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This contradicts readme, which says that "An enqueue failure leaves the stream active". The failed record was never queued, so there is nothing to recover?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. An enqueue failure never queued that payload, so there is nothing to recover for that call. The changelog now matches the README: close first only if you need to inspect records that were already accepted.

Comment thread dotnet/README.md

## Installation

### NuGet (when published)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it published yet? Maybe better to leave it like this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not published yet. Restored "NuGet (when published)" and dropped the version pin.

elenagaljak-db
elenagaljak-db previously approved these changes Aug 14, 2026
Signed-off-by: teodordelibasic-db <teodor.delibasic@databricks.com>
@teodordelibasic-db
teodordelibasic-db added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit 1444541 Aug 14, 2026
169 of 170 checks passed
@teodordelibasic-db
teodordelibasic-db deleted the codex/audit-sdk-snippets branch August 14, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants