[All] Correct SDK documentation and examples - #729
Conversation
0e03af6 to
9a004c1
Compare
| # ack = stream.ingest_record(record_dict) # Deprecated | ||
| # offset = ack.wait_for_ack() # Extra step needed | ||
|
|
||
| end_time = time.time() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| unacked = stream.get_unacked_records() # Returns List[bytes] | ||
| stream.close() | ||
| except ZerobusException as e: | ||
| unacked = list(stream.get_unacked_records()) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| 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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| ```python | ||
| unacked_batches = stream.get_unacked_batches() # Returns List[List[bytes]] |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.):** |
There was a problem hiding this comment.
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().
There was a problem hiding this comment.
The exported HeadersProvider is now the shape createStream() actually accepts: getHeadersCallback returning header tuples synchronously. The docs no longer show an async getHeaders() class.
| /// | ||
| /// * `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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| // 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}`); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Updated: outer finally always closes the original stream, recovery only runs after a failure, and the replacement stream is closed in a nested finally.
| AirQuality.create({ deviceName: 'sensor-002', temp: 23, humidity: 67 }), | ||
| AirQuality.create({ deviceName: 'sensor-003', temp: 24, humidity: 69 }) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Proto and JSON batch examples now queue every demonstration batch, then flush() once.
|
|
||
| ### Code Highlights | ||
|
|
||
| **Offset-based API (Recommended):** |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
|
||
| main().catch((error) => { | ||
| console.error('Fatal error:', error); | ||
| }); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
main().catch now sets process.exitCode = 1.
|
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"}') |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Changed to “Submission acknowledged”.
Signed-off-by: teodordelibasic-db <teodor.delibasic@databricks.com>
|
|
||
| ```bash | ||
| dotnet add package Databricks.Zerobus.Ingest.Sdk | ||
| dotnet add package Databricks.Zerobus --version 0.5.1 |
There was a problem hiding this comment.
Why 0.5.1? Databricks.Zerobus is incorrect
There was a problem hiding this comment.
Fixed. The package id is Databricks.Zerobus.Ingest.Sdk. Left it unpinned under "NuGet (when published)" since it is not on NuGet yet.
| // 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 | ||
| // |
There was a problem hiding this comment.
The important note and the second bullet contradict
There was a problem hiding this comment.
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.
| - Documented that `get_unacked_records()` and `recreate_stream()` require a closed | ||
| stream, and that enqueue failures must be closed before recovery. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
|
|
||
| ## Installation | ||
|
|
||
| ### NuGet (when published) |
There was a problem hiding this comment.
Is it published yet? Maybe better to leave it like this?
There was a problem hiding this comment.
Not published yet. Restored "NuGet (when published)" and dropped the version pin.
Signed-off-by: teodordelibasic-db <teodor.delibasic@databricks.com>
786638c
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:
_zerobus_core.pyistill has wrongget_unacked_records()return types,create_streamargument order, async signatures, and is missing row-streamget_unacked_batches().stream_idandget_state()still return placeholder values (stream-placeholder-idand constantOPENED).AckCallbackexceptions but does not clear the pending Java exception, which can poison later callback operations.setRecoveryBackoffMsdoes not reject negative values.NativeTestHelperremains a public production class despite exposing test-only JNI symbols.ingest_record_nowait/ingest_records_nowaitare documented as unsafe but do not emit a deprecation warning.go/version.goremains1.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.