booblik
Wiki

clients

Generated page

Model gemma-mtp, commit ef58254ca7be, 2026-08-16, sources: 6. Edit the code or the hand-written documentation instead.

What this module is responsible for

The clients module provides a suite of client libraries for the booblik protocol, ensuring that various programming languages can interact with the broker as both producers and consumers.

Diagram

Client Implementations

The module provides six client libraries, five of which are reimplementations of the protocol and one which is a multiplatform target (README.md:21). All six libraries are designed to act as both producer and consumer, and all six must pass the same fourteen conformance checks (README.md:6-7).

LanguageCoordinateRolesDependencies
Gogithub.com/youndie/booblik/clients/goproducer, consumernone
Pythonbooblik on PyPIproducer, consumernone
Python-asynciobooblik.aioproducer, consumernone
Node.jsbooblik on npmproducer, consumernone
.NETBooblik on NuGetproducer, consumerxunit (tests)
Javabooblik-java on reposiliteproducer, consumerJUnit (tests)
Kotlin/Nativebooblik-native on reposiliteproducer, consumernone

Kotlin/Native Multiplatform Target

Unlike the other clients, the Kotlin/Native client is not a reimplementation but a target of the shared booblik-protocol (README.md:5-8). It shares the codec, the IDs, and the partitioner with the JVM client, allowing it to compile for both linuxX64 and macosArm64 from a single source (README.md:22-23).

More: Kotlin/Native Multiplatform Target

Batching and Throughput

Performance is heavily dependent on batching; sending one record per request is considered the most expensive mistake possible (README.md:22-23). The broker's measurements show that batches of a hundred can achieve 4,335,482 records/s, compared to only 80,592 records/s when sent one at a time (README.md:23).

Producer and Consumer Mechanics

In the .NET implementation, a Producer owns its Connection and acts as the sole writer to the socket; using the same Connection directly while a Producer is active can lead to mismatched responses (README.md:44-45). For reading, the .NET client uses IAsyncEnumerable to provide back-pressure, ensuring the next fetch does not occur until the current loop body is finished (README.md:81-82).

More: Producer and Consumer Mechanics

CRC-32C Checksum Implementation

Checksum verification requires handling language-specific "traps" regarding integer types and hardware instructions (README.md:37-40).

LanguageImplementation Detail
GoUses standard library with hardware instruction (README.md:42-43)
PythonRequires reading the stored sum as unsigned (README.md:51)
JavaScriptRequires >>> 0 because bitwise operators produce signed 32-bit results (README.md:51)
JavaRequires a cast from CRC32C.getValue()'s long (README.md:52)
C#Uses unchecked because FNV-1a arithmetic must wrap at 32 bits (README.md:65-66)
Kotlin/NativeUses a reflected polynomial in a 256-entry table (README.md:77-78)

More: CRC-32C Checksum Implementation

Conformance and Gatekeeping

Each client directory must contain gate.sh for ecosystem-specific checks and conformance-client.sh to execute the client under test (README.md:79-80). The conformance-client.sh script for .NET builds the assembly only if the source files are newer than the existing DLL (conformance-client.sh:13-14).

Key files

FileLinesWhat is there
clients/README.md13-19Table of client coordinates, roles, and dependencies
…/dotnet/README.md20-42Documentation on batching and the Producer class
…/dotnet/gate.sh22-23Command to run dotnet test
…/kotlin-native/README.md28-31Explanation of the synchronous nature of the Native client
…/kotlin-native/conformance-client.sh18-31Logic to select the correct TARGET and LINK_TASK based on the host OS

Behaviour that surprise

  • AckPolicy.None in .NET results in nothing being returned at all, as no offset exists until the writer reaches the batch (README.md:51-52).
  • The key is never actually sent to the broker; the client calculates the partition and sends the number instead (README.md:55-56).
  • PartitionFor(null) in both .NET and Kotlin/Native advances a round-robin counter, meaning the first call and the subsequent send constitute two turns of the counter (README.md:62 and README.md:95).

On this page