Skip to content

[All] Correct SDK documentation and examples - #729

Open
teodordelibasic-db wants to merge 11 commits into
mainfrom
codex/audit-sdk-snippets
Open

[All] Correct SDK documentation and examples#729
teodordelibasic-db wants to merge 11 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.

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
Signed-off-by: teodordelibasic-db <teodor.delibasic@databricks.com>
# 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.

Comment thread python/README.md
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.

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.

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

* );
* ```
*
* **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().

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.

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.

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.


### 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.

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.

@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.

@nikolaobradovic-db

Copy link
Copy Markdown
Contributor

LLM instrumented pass from my side about gaps not covered by this PR, includes also high severity bugs in SDK:
P0 — code defect exposed by stale docs
Java recovery silently loses unacknowledged data
java/src/main/java/com/databricks/zerobus/BaseZerobusStream.java:108-135
ZerobusArrowStream.java:175-200
JNI nativeClose() removes the Rust stream before Java retrieves unacked data, so Java catches the retrieval failure and caches an empty list. Recovery can silently omit records. This requires an implementation fix, not only documentation.
P1 — Go and pure Go
Impossible post-close recovery flow
go/README.md:713-725,887-896,1221-1255
go/zerobus.go:722-755
Close() clears/frees the handle, but docs instruct users to call GetUnackedRecords() afterward.

Consumer installation incorrectly requires Rust and go generate
go/README.md:79-116, go/zerobus.go:7-15
Bundled archives support normal consumers. Rust is only needed for source/contributor builds. Some documentation also still advertises Go 1.19 instead of required Go 1.21+.

make build-go is not Go-only
go/README.md:1480-1488, go/CONTRIBUTING.md:85-98, go/Makefile:62-65
The target depends on build-rust.

Go user-agent version is stale
go/version.go:3-4 remains 1.3.0, while the released source is v1.4.0 and the next changelog targets v1.5.0.

RecordAck.Await() is documented as immediate
go/README.md:1258-1273,1313-1325
It actually calls WaitForOffset() and blocks for server durability. Only Offset() is immediate.

Go concurrency guidance contradicts the API
go/README.md:945-962 says one stream per goroutine, while go/zerobus.go:147-149 explicitly supports concurrent ingestion.

Go copyable snippets omit durability barriers
go/examples/README.md:105-150, go/README.md:572-600
They queue records but do not flush.

Pure-Go recovery treats every flush error as terminal
purego/examples/json/single/main.go:66-83
A flush timeout can leave the stream active, making unacked retrieval fail.

Pure-Go batch callback semantics are wrong
purego/examples/json/batch/main.go:29-35,82-89
A batch produces one callback event, not one per record, and callback delivery may still be running when Close() returns.

P1 — Java
Migration guide preserves per-record round trips
java/README.md:794-805, java/examples/legacy/README.md:63-92
It replaces .join() with immediate waitForOffset(). It should retain the last offset and wait/flush once.

Callback exceptions are not contained as documented
java/src/main/java/com/databricks/zerobus/AckCallback.java:40-60
JNI logs Java exceptions but does not clear the pending exception, potentially poisoning later callback operations.

flush() is presented as a callback-drain barrier
java/README.md:1571-1602
Flush waits for durability, not callback completion. Close can abort callback work after its fixed five-second budget.

Source-build/example commands produce non-runnable artifacts
java/README.md:214-236, java/examples/README.md:145-205
-Dzerobus.skipNativeLibCheck=true skips validation but does not package JNI libraries.

macOS is advertised but not released
java/README.md:74-88
The JNI release workflow currently builds only Linux and Windows artifacts.

Java 8 source-build requirement is false
java/README.md:120-124, java/CONTRIBUTING.md:9-29
Tests/build tooling require at least JDK 11. Runtime compatibility should be documented separately.

Proto examples depend on a missing generated file
java/examples/README.md:20-24,154-172
AirQualityProto.java is described as pre-generated but is absent.

GenerateProto overclaims STRUCT support
java/README.md:401-424, java/tools/README.md:11-18
The implementation does not recursively support the documented nested structures.

GenerateProto commands use stale artifact names/version
java/tools/README.md:48-75,108-170,237-251 references 0.1.0 instead of zerobus-ingest-sdk 1.3.0.

Configuration defaults are stale
java/README.md:874-883,1418-1441
It documents recoveryRetries=3 and elsewhere maxInflightRecords=50000; implementation defaults are 4 and 1,000,000.

Negative recovery backoff validation is documented but absent
StreamConfigurationOptions.java:285-297
Negative values are cast to u64 in JNI, becoming extremely large delays.

Several copyable stream snippets can leak JNI handles
java/README.md:561-590,628-704, java/examples/README.md:49-116
Java has no finalizers; these should use try-with-resources.

Primary README omits Java thread-safety constraints
Java SDK and streams are not thread-safe, but java/README.md:1604-1619 does not mention required external synchronization.

P1 — Python and TypeScript
Python native stubs do not match runtime APIs
python/zerobus/_zerobus_core.pyi:283-302,321-338,436-455
Problems include get_unacked_records() -> int, incorrect async declarations, nonexistent timeout arguments, wrong create_stream ordering, and missing get_unacked_batches().

Python exposes fabricated stream IDs and states
python/zerobus/sdk/{sync,aio}/zerobus_sdk.py returns placeholder IDs and constant OPENED, which can mislead monitoring/recovery logic.

TypeScript TableProperties docs select the wrong format
typescript/src/lib.rs:95-107 says omitting the descriptor selects JSON. Runtime defaults to Proto and rejects the missing descriptor unless RecordType.Json is explicitly configured.

TypeScript inflight default is wrong by 100×
typescript/src/lib.rs:52-54, typescript/README.md:1033-1056 says 10,000; actual inherited Rust default is 1,000,000.

TypeScript claims GC eliminates cleanup
typescript/README.md:1105-1109 says “No manual cleanup required.” GC reclaims memory but does not flush; users must call await stream.close().

P1 — Rust, C++, .NET and C FFI
Rust batch examples serialize every batch
rust/examples/json/batch.rs and rust/examples/proto/compiled/batch.rs immediately wait after each batch instead of queueing all batches and flushing once.

Primary Rustdoc still teaches per-ingest waits
rust/sdk/src/sdk.rs:26-50, stream/grpc/acks.rs:188-208.

Rust docs reference removed APIs
rust/sdk/src/stream/grpc/ingest.rs:17-21,66-70 and example READMEs still mention nonexistent ingest_record()/ingest_records() methods.

C++ recovery fails after a flush timeout
cpp/examples/json/single.cpp:137-145
It immediately retrieves unacked records although a timeout can leave the stream active.

.NET recovery doc has the same active-stream error
dotnet/src/Zerobus/ZerobusStream.cs:234-247.

.NET JSON example can report partial ingestion as success
dotnet/examples/JsonSingle/Program.cs:47-66 swallows ingest failures and still prints success.

First .NET API examples omit durability confirmation
dotnet/src/Zerobus/ZerobusStream.cs:53-77,114-131 stop after queueing.

C FFI README omits the required lifecycle
rust/ffi/README.md:90-111 does not show flush, close/free, SDK free, or complete error-string cleanup.

P2 — smaller stale items
go/README.md:145 calls offset admission “per-record acknowledgment.”
Go batch examples call the single batch offset lastOffset.
Go package godoc still uses deprecated IngestRecord() in primary error handling.
Go example modules require Go 1.24–1.25 despite the SDK minimum being 1.21.
Java README incorrectly says Maven users must manually add compile-scope transitive dependencies.
C++ Arrow docs describe schema validation at the wrong lifecycle point.
rust/tools/generate_files/README.md:52-60 contains malformed shell quoting.
dotnet/CONTRIBUTING.md:97-102 points to nonexistent src/Zerobus/Interop/; the code is under Native/.
The Java recovery defect should be fixed before relying on any Java recovery documentation. The remaining items can be split into follow-up PRs by SDK if the current documentation PR is already too broad.

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.

2 participants