-
Notifications
You must be signed in to change notification settings - Fork 1
v2.10.0-gr-experimental - Alloc-Free Consumer and Producer API #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
name: 'confluent-kafka-dotnet build pipeline' | ||
|
||
env: | ||
DOTNET_CLI_TELEMETRY_OPTOUT: 'true' | ||
|
||
on: | ||
push: | ||
pull_request: | ||
|
||
jobs: | ||
|
||
build-test: | ||
strategy: | ||
matrix: | ||
os: [ubuntu-latest, windows-latest, macos-13] # macos-13 for x86_x64 arch | ||
runs-on: ${{ matrix.os }} | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Setup .NET | ||
uses: actions/setup-dotnet@v4 | ||
with: | ||
dotnet-version: | | ||
6.0.x | ||
8.0.x | ||
- name: Build and test | ||
run: | | ||
dotnet nuget add source --username user --password ${{ github.token }} --store-password-in-clear-text --name github https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json | ||
dotnet restore | ||
dotnet test -c Release test/Confluent.Kafka.UnitTests/Confluent.Kafka.UnitTests.csproj | ||
|
||
package: | ||
needs: [build-test] | ||
runs-on: windows-latest | ||
steps: | ||
|
||
- name: Show default environment variables | ||
run: | | ||
echo "The job_id is: $GITHUB_JOB" # reference the default environment variables | ||
echo "The id of this action is: $GITHUB_ACTION" # reference the default environment variables | ||
echo "The run id is: $GITHUB_RUN_ID" | ||
echo "The GitHub Actor's username is: $GITHUB_ACTOR" | ||
echo "GitHub SHA: $GITHUB_SHA" | ||
- uses: actions/checkout@v4 | ||
- name: Setup .NET | ||
uses: actions/setup-dotnet@v4 | ||
with: | ||
dotnet-version: | | ||
6.0.x | ||
8.0.x | ||
- name: Build and create packages | ||
run: | | ||
dotnet restore | ||
dotnet build Confluent.Kafka.sln -c Release | ||
|
||
# Different packaging for tagged vs untagged builds | ||
if ($env:GITHUB_REF -match '^refs/tags/') { | ||
$suffix = "gr-experimental" | ||
} else { | ||
$suffix = "ci-$env:GITHUB_RUN_ID" | ||
} | ||
|
||
dotnet pack src/Confluent.Kafka/Confluent.Kafka.csproj --output dist -c Release --version-suffix $suffix | ||
|
||
- name: Upload artifacts | ||
uses: actions/upload-artifact@v4 | ||
with: | ||
name: build-artifacts | ||
path: dist/ | ||
|
||
# Publish NuGet packages when a tag is pushed. | ||
# Tests need to succeed for all components and on all platforms first, | ||
# including having a tag name that matches the version number. | ||
publish-release: | ||
if: ${{ startsWith(github.ref, 'refs/tags/v') }} | ||
needs: package | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Download NuGet package artifacts | ||
uses: actions/download-artifact@v4 | ||
with: | ||
name: build-artifacts | ||
path: dist | ||
- name: Publish to NuGet | ||
run: | | ||
dotnet nuget push "dist/Confluent.Kafka*.nupkg" --source https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json --api-key ${{ github.token }} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
using System; | ||
using Confluent.Kafka.Impl; | ||
using Confluent.Kafka.Internal; | ||
|
||
namespace Confluent.Kafka; | ||
|
||
/// <summary> | ||
/// Experimental alloc-free APIs. | ||
/// Note that these APIs can be changed substantially or removed entirely in the future versions. | ||
/// </summary> | ||
public abstract class Experimental | ||
{ | ||
/// <summary> | ||
/// Callback delegate for allocation-free message consumption. | ||
/// </summary> | ||
/// <typeparam name="TKey">The key type</typeparam> | ||
Check warning on line 16 in src/Confluent.Kafka/Experimental.cs
|
||
/// <typeparam name="TValue">The value type</typeparam> | ||
Check warning on line 17 in src/Confluent.Kafka/Experimental.cs
|
||
/// <param name="reader">Message reader providing allocation-free access to message data</param> | ||
public delegate void AllocFreeConsumeCallback(in MessageReader reader); | ||
|
||
/// <summary> | ||
/// Alloc-free delivery handler | ||
/// </summary> | ||
public delegate void AllocFreeDeliveryHandler(in MessageReader arg); | ||
|
||
/// <summary> | ||
/// Allocation-free message reader that provides access to consumed message data. | ||
/// This is a ref struct to ensure stack allocation and prevent escaping the callback scope. | ||
/// </summary> | ||
public readonly unsafe ref struct MessageReader | ||
{ | ||
private readonly rd_kafka_message* msg; | ||
private readonly Lazy<IntPtr> _hdrsPtr; | ||
|
||
internal MessageReader( | ||
rd_kafka_message* msg) | ||
{ | ||
this.msg = msg; | ||
_hdrsPtr = new Lazy<IntPtr>(() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realised that this is not alloc-free. Will update it. |
||
{ | ||
var ptr = (IntPtr)msg; | ||
IntPtr hdrsPtr; | ||
Librdkafka.message_headers(ptr, out hdrsPtr); | ||
return hdrsPtr; | ||
}); | ||
} | ||
|
||
/// <summary>Gets the topic name</summary> | ||
public string Topic => | ||
msg->rkt != IntPtr.Zero | ||
? Util.Marshal.PtrToStringUTF8(Librdkafka.topic_name(msg->rkt)) | ||
: null; | ||
|
||
/// <summary>Gets the partition</summary> | ||
public Partition Partition => msg->partition; | ||
|
||
/// <summary>Gets the error code</summary> | ||
public ErrorCode ErrorCode => msg->err; | ||
|
||
/// <summary>Gets the offset</summary> | ||
public Offset Offset => msg->offset; | ||
|
||
/// <summary>Gets the leader epoch</summary> | ||
public int? LeaderEpoch => | ||
msg->rkt != IntPtr.Zero && msg->offset != Offset.Unset | ||
? Librdkafka.message_leader_epoch((IntPtr)msg) | ||
: null; | ||
|
||
/// <summary>Gets the timestamp</summary> | ||
public Timestamp Timestamp | ||
{ | ||
get | ||
{ | ||
var timestampUnix = Librdkafka.message_timestamp((IntPtr)msg, out var timestampType); | ||
return new Timestamp(timestampUnix, (TimestampType)timestampType); | ||
} | ||
} | ||
|
||
/// <summary>Gets whether this indicates partition EOF</summary> | ||
public bool IsPartitionEOF => msg->err == ErrorCode.Local_PartitionEOF; | ||
|
||
/// <summary> | ||
/// Gets the raw key data as a ReadOnlySpan without allocation | ||
/// </summary> | ||
public ReadOnlySpan<byte> KeySpan => | ||
msg->key == IntPtr.Zero | ||
? ReadOnlySpan<byte>.Empty | ||
: new ReadOnlySpan<byte>(msg->key.ToPointer(), (int)msg->key_len); | ||
|
||
/// <summary> | ||
/// Gets the raw value data as a ReadOnlySpan without allocation | ||
/// </summary> | ||
public ReadOnlySpan<byte> ValueSpan => | ||
msg->val == IntPtr.Zero | ||
? ReadOnlySpan<byte>.Empty | ||
: new ReadOnlySpan<byte>(msg->val.ToPointer(), (int)msg->len); | ||
|
||
/// <summary> | ||
/// Gets a header by index without allocation | ||
/// </summary> | ||
/// <param name="index">Zero-based header index</param> | ||
/// <param name="name">Header name (allocated string)</param> | ||
/// <param name="value">Header value as ReadOnlySpan</param> | ||
/// <returns>True if header exists at the given index</returns> | ||
public bool TryGetHeader(int index, out string name, out ReadOnlySpan<byte> value) | ||
{ | ||
name = null; | ||
value = ReadOnlySpan<byte>.Empty; | ||
|
||
var hdrsPtr = _hdrsPtr.Value; | ||
if (hdrsPtr == IntPtr.Zero) | ||
return false; | ||
|
||
var err = Librdkafka.header_get_all( | ||
hdrsPtr, | ||
(IntPtr)index, | ||
out IntPtr namep, | ||
out IntPtr valuep, | ||
out IntPtr sizep | ||
); | ||
|
||
if (err != ErrorCode.NoError) | ||
return false; | ||
|
||
name = Util.Marshal.PtrToStringUTF8(namep); | ||
|
||
if (valuep != IntPtr.Zero) | ||
{ | ||
value = new ReadOnlySpan<byte>(valuep.ToPointer(), (int)sizep); | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gr-experimental
suffix