karafka-rdkafka 0.20.0.rc2 → 0.20.0.rc5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci_linux_x86_64_gnu.yml +249 -0
  3. data/.github/workflows/ci_linux_x86_64_musl.yml +205 -0
  4. data/.github/workflows/ci_macos_arm64.yml +306 -0
  5. data/.github/workflows/push_linux_x86_64_gnu.yml +64 -0
  6. data/.github/workflows/push_linux_x86_64_musl.yml +77 -0
  7. data/.github/workflows/push_macos_arm64.yml +54 -0
  8. data/.github/workflows/push_ruby.yml +37 -0
  9. data/.gitignore +1 -0
  10. data/.ruby-version +1 -1
  11. data/CHANGELOG.md +22 -3
  12. data/README.md +2 -3
  13. data/Rakefile +0 -2
  14. data/dist/{librdkafka-2.10.0.tar.gz → librdkafka-2.8.0.tar.gz} +0 -0
  15. data/docker-compose.yml +1 -1
  16. data/ext/Rakefile +1 -1
  17. data/ext/build_common.sh +361 -0
  18. data/ext/build_linux_x86_64_gnu.sh +306 -0
  19. data/ext/build_linux_x86_64_musl.sh +763 -0
  20. data/ext/build_macos_arm64.sh +550 -0
  21. data/karafka-rdkafka.gemspec +26 -10
  22. data/lib/rdkafka/bindings.rb +31 -4
  23. data/lib/rdkafka/config.rb +4 -1
  24. data/lib/rdkafka/error.rb +8 -1
  25. data/lib/rdkafka/native_kafka.rb +4 -0
  26. data/lib/rdkafka/producer/partitions_count_cache.rb +216 -0
  27. data/lib/rdkafka/producer.rb +40 -28
  28. data/lib/rdkafka/version.rb +3 -3
  29. data/lib/rdkafka.rb +1 -0
  30. data/renovate.json +74 -0
  31. data/spec/rdkafka/admin_spec.rb +15 -2
  32. data/spec/rdkafka/bindings_spec.rb +0 -1
  33. data/spec/rdkafka/config_spec.rb +1 -1
  34. data/spec/rdkafka/consumer_spec.rb +35 -14
  35. data/spec/rdkafka/metadata_spec.rb +2 -2
  36. data/spec/rdkafka/producer/partitions_count_cache_spec.rb +359 -0
  37. data/spec/rdkafka/producer/partitions_count_spec.rb +359 -0
  38. data/spec/rdkafka/producer_spec.rb +198 -7
  39. data/spec/spec_helper.rb +12 -1
  40. metadata +43 -100
  41. checksums.yaml.gz.sig +0 -0
  42. data/.github/workflows/ci.yml +0 -99
  43. data/Guardfile +0 -19
  44. data/certs/cert.pem +0 -26
  45. data.tar.gz.sig +0 -0
  46. metadata.gz.sig +0 -3
@@ -0,0 +1,550 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Simple librdkafka build script for macOS with Kerberos support
4
+ # Usage: ./build-librdkafka-macos.sh
5
+ #
6
+ # Expected directory structure:
7
+ # ext/build_macos.sh (this script)
8
+ # ext/build-common.sh (shared functions)
9
+ # dist/librdkafka-*.tar.gz (librdkafka source tarball)
10
+ # dist/patches/*.patch (optional Ruby-specific patches)
11
+ #
12
+ set -euo pipefail
13
+
14
+ # Source common functions and constants
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ source "$SCRIPT_DIR/build_common.sh"
17
+
18
+ # Platform-specific paths
19
+ DIST_DIR="$SCRIPT_DIR/../dist"
20
+ PATCHES_DIR="$DIST_DIR/patches"
21
+ BUILD_DIR="$(pwd)/build-tmp-macos"
22
+ DEPS_PREFIX="/tmp/macos-deps"
23
+
24
+ # macOS-specific dependency check
25
+ check_macos_dependencies() {
26
+ log "Checking macOS build dependencies..."
27
+
28
+ # Check for Xcode Command Line Tools
29
+ if ! xcode-select -p &> /dev/null; then
30
+ error "Xcode Command Line Tools not found. Install with: xcode-select --install"
31
+ fi
32
+
33
+ # Check for required tools (in addition to common ones)
34
+ local missing_tools=()
35
+
36
+ command -v gcc &> /dev/null || missing_tools+=("gcc")
37
+ command -v clang &> /dev/null || missing_tools+=("clang")
38
+
39
+ if [ ${#missing_tools[@]} -gt 0 ]; then
40
+ error "Missing required tools: ${missing_tools[*]}"
41
+ fi
42
+
43
+ log "✅ All required tools found"
44
+
45
+ # Show system info
46
+ log "Build environment:"
47
+ log " - macOS version: $(sw_vers -productVersion)"
48
+ log " - Architecture: $(uname -m)"
49
+ log " - Xcode tools: $(xcode-select -p)"
50
+ }
51
+
52
+ # macOS-specific compiler setup
53
+ setup_macos_compiler() {
54
+ local arch="$1"
55
+
56
+ # Get the proper macOS SDK path
57
+ MACOS_SDK_PATH=$(xcrun --show-sdk-path)
58
+ log "Using macOS SDK: $MACOS_SDK_PATH"
59
+
60
+ # Set macOS-specific flags
61
+ export CC="$(xcrun -find clang)"
62
+ export CFLAGS="-fPIC -O2 -arch $arch -isysroot $MACOS_SDK_PATH"
63
+ export CXXFLAGS="-fPIC -O2 -arch $arch -isysroot $MACOS_SDK_PATH"
64
+ export CPPFLAGS="-isysroot $MACOS_SDK_PATH"
65
+
66
+ log "Applied $arch specific flags"
67
+ }
68
+
69
+ # Build static OpenSSL for macOS
70
+ build_openssl_macos() {
71
+ local arch="$1"
72
+ local openssl_prefix="$2"
73
+ local openssl_dir="$3"
74
+
75
+ cd "$openssl_dir"
76
+
77
+ if [ ! -f "$openssl_prefix/lib/libssl.a" ]; then
78
+ log "Configuring and building static OpenSSL..."
79
+ make clean 2>/dev/null || true
80
+
81
+ setup_macos_compiler "$arch"
82
+
83
+ # Configure OpenSSL for macOS
84
+ if [ "$arch" = "arm64" ]; then
85
+ ./Configure darwin64-arm64-cc \
86
+ no-shared \
87
+ no-dso \
88
+ --prefix="$openssl_prefix" \
89
+ --openssldir="$openssl_prefix/ssl"
90
+ else
91
+ ./Configure darwin64-x86_64-cc \
92
+ no-shared \
93
+ no-dso \
94
+ --prefix="$openssl_prefix" \
95
+ --openssldir="$openssl_prefix/ssl"
96
+ fi
97
+
98
+ make -j$(get_cpu_count)
99
+ make install
100
+
101
+ # Verify the build
102
+ if [ ! -f "$openssl_prefix/lib/libssl.a" ] || [ ! -f "$openssl_prefix/lib/libcrypto.a" ]; then
103
+ error "Failed to build static OpenSSL"
104
+ fi
105
+
106
+ log "✅ Static OpenSSL built successfully at $openssl_prefix"
107
+ else
108
+ log "Static OpenSSL already built, skipping..."
109
+ fi
110
+ }
111
+
112
+ # Build static MIT Kerberos for macOS
113
+ build_krb5_macos() {
114
+ local arch="$1"
115
+ local krb5_prefix="$2"
116
+ local krb5_dir="$3"
117
+
118
+ cd "$krb5_dir/src"
119
+
120
+ if [ ! -f "$krb5_prefix/lib/libgssapi_krb5.a" ]; then
121
+ log "Configuring and building static MIT Kerberos..."
122
+ make clean 2>/dev/null || true
123
+
124
+ setup_macos_compiler "$arch"
125
+
126
+ # Configure MIT Kerberos for macOS
127
+ ./configure \
128
+ --disable-shared \
129
+ --enable-static \
130
+ --prefix="$krb5_prefix" \
131
+ --without-ldap \
132
+ --without-tcl \
133
+ --without-keyutils \
134
+ --disable-rpath \
135
+ --without-system-verto \
136
+ --disable-thread-support \
137
+ --disable-aesni
138
+
139
+ # Build everything except the problematic kadmin tools (same as Linux)
140
+ log "Building Kerberos (will ignore kadmin build failures)..."
141
+ make -j$(get_cpu_count) || {
142
+ log "Full build failed (expected due to kadmin), continuing with libraries..."
143
+ # The libraries should be built even if kadmin fails
144
+ true
145
+ }
146
+
147
+ # Install what was successfully built
148
+ make install || {
149
+ log "Full install failed, installing individual components..."
150
+ # Try to install the core libraries manually
151
+ make install-mkdirs 2>/dev/null || true
152
+ make -C util install 2>/dev/null || true
153
+ make -C lib install 2>/dev/null || true
154
+ make -C plugins/kdb/db2 install 2>/dev/null || true
155
+ }
156
+
157
+ # Verify we got the essential libraries
158
+ if [ ! -f "$krb5_prefix/lib/libgssapi_krb5.a" ]; then
159
+ error "Failed to build essential Kerberos libraries"
160
+ fi
161
+
162
+ log "✅ Static MIT Kerberos built successfully at $krb5_prefix"
163
+ else
164
+ log "Static MIT Kerberos already built, skipping..."
165
+ fi
166
+ }
167
+
168
+ # Build static Cyrus SASL for macOS with Kerberos support
169
+ build_sasl_macos() {
170
+ local arch="$1"
171
+ local sasl_prefix="$2"
172
+ local sasl_dir="$3"
173
+ local openssl_prefix="$4"
174
+ local krb5_prefix="$5"
175
+
176
+ cd "$sasl_dir"
177
+
178
+ if [ ! -f "$sasl_prefix/lib/libsasl2.a" ]; then
179
+ log "Configuring and building static Cyrus SASL with Kerberos support..."
180
+ make clean 2>/dev/null || true
181
+
182
+ setup_macos_compiler "$arch"
183
+ export CPPFLAGS="$CPPFLAGS -I$openssl_prefix/include -I$krb5_prefix/include"
184
+ export LDFLAGS="-L$openssl_prefix/lib -L$krb5_prefix/lib"
185
+
186
+ # Configure SASL with Kerberos/GSSAPI support (now ENABLED)
187
+ ./configure \
188
+ --disable-shared \
189
+ --enable-static \
190
+ --prefix="$sasl_prefix" \
191
+ --without-dblib \
192
+ --disable-gdbm \
193
+ --disable-macos-framework \
194
+ --disable-sample \
195
+ --disable-obsolete_cram_attr \
196
+ --disable-obsolete_digest_attr \
197
+ --enable-gssapi="$krb5_prefix" \
198
+ --disable-krb4 \
199
+ --with-openssl="$openssl_prefix"
200
+
201
+ make -j$(get_cpu_count)
202
+ make install
203
+
204
+ # Verify the build
205
+ if [ ! -f "$sasl_prefix/lib/libsasl2.a" ]; then
206
+ error "Failed to build static Cyrus SASL"
207
+ fi
208
+
209
+ log "✅ Static Cyrus SASL with Kerberos support built successfully at $sasl_prefix"
210
+ else
211
+ log "Static Cyrus SASL already built, skipping..."
212
+ fi
213
+ }
214
+
215
+ # Build generic static library for macOS
216
+ build_static_lib_macos() {
217
+ local lib_name="$1"
218
+ local arch="$2"
219
+ local prefix="$3"
220
+ local source_dir="$4"
221
+ local configure_args="$5"
222
+
223
+ cd "$source_dir"
224
+
225
+ local lib_file="$prefix/lib/lib${lib_name}.a"
226
+ if [ ! -f "$lib_file" ]; then
227
+ log "Configuring and building static $lib_name..."
228
+ make clean 2>/dev/null || true
229
+
230
+ setup_macos_compiler "$arch"
231
+
232
+ # Run configure with provided arguments
233
+ eval "./configure --prefix=\"$prefix\" $configure_args"
234
+
235
+ make -j$(get_cpu_count)
236
+ make install
237
+
238
+ # Verify the build
239
+ if [ ! -f "$lib_file" ]; then
240
+ error "Failed to build static $lib_name"
241
+ fi
242
+
243
+ log "✅ Static $lib_name built successfully at $prefix"
244
+ else
245
+ log "Static $lib_name already built, skipping..."
246
+ fi
247
+ }
248
+
249
+ # Check common and macOS-specific dependencies
250
+ check_common_dependencies
251
+ check_macos_dependencies
252
+
253
+ # Auto-detect librdkafka tarball
254
+ log "Looking for librdkafka tarball in $DIST_DIR..."
255
+ LIBRDKAFKA_TARBALL=$(find_librdkafka_tarball "$DIST_DIR")
256
+ log "Found librdkafka tarball: $LIBRDKAFKA_TARBALL"
257
+
258
+ # Verify librdkafka tarball checksum if available
259
+ verify_librdkafka_checksum "$LIBRDKAFKA_TARBALL"
260
+
261
+ # Find patches
262
+ PATCHES_FOUND=()
263
+ find_patches "$PATCHES_DIR" PATCHES_FOUND
264
+
265
+ # Detect architecture early since we need it for dependency building
266
+ ARCH=$(uname -m)
267
+ log "Detected architecture: $ARCH"
268
+
269
+ security_log "Starting secure build with checksum verification enabled"
270
+ log "Building self-contained librdkafka for macOS with Kerberos support"
271
+ log "Dependencies to build:"
272
+ log " - OpenSSL: $OPENSSL_VERSION"
273
+ log " - Cyrus SASL: $CYRUS_SASL_VERSION (with Kerberos support)"
274
+ log " - MIT Kerberos: $KRB5_VERSION"
275
+ log " - zlib: $ZLIB_VERSION"
276
+ log " - ZStd: $ZSTD_VERSION"
277
+ log "librdkafka source: $LIBRDKAFKA_TARBALL"
278
+ log "Build directory: $BUILD_DIR"
279
+
280
+ # Create build directory
281
+ mkdir -p "$BUILD_DIR"
282
+ cd "$BUILD_DIR"
283
+
284
+ # Build static OpenSSL first (other deps might need it)
285
+ log "Building static OpenSSL $OPENSSL_VERSION..."
286
+ OPENSSL_PREFIX="$DEPS_PREFIX/static-openssl-$OPENSSL_VERSION"
287
+ OPENSSL_TARBALL="openssl-$OPENSSL_VERSION.tar.gz"
288
+ OPENSSL_DIR="openssl-$OPENSSL_VERSION"
289
+
290
+ secure_download "$(get_openssl_url)" "$OPENSSL_TARBALL"
291
+ extract_if_needed "$OPENSSL_TARBALL" "$OPENSSL_DIR"
292
+ build_openssl_macos "$ARCH" "$OPENSSL_PREFIX" "$OPENSSL_DIR"
293
+
294
+ cd "$BUILD_DIR"
295
+
296
+ # Build static MIT Kerberos (before SASL since SASL needs it)
297
+ log "Building static MIT Kerberos $KRB5_VERSION..."
298
+ KRB5_PREFIX="$DEPS_PREFIX/static-krb5-$KRB5_VERSION"
299
+ KRB5_TARBALL="krb5-$KRB5_VERSION.tar.gz"
300
+ KRB5_DIR="krb5-$KRB5_VERSION"
301
+
302
+ secure_download "$(get_krb5_url)" "$KRB5_TARBALL"
303
+ extract_if_needed "$KRB5_TARBALL" "$KRB5_DIR"
304
+ build_krb5_macos "$ARCH" "$KRB5_PREFIX" "$KRB5_DIR"
305
+
306
+ cd "$BUILD_DIR"
307
+
308
+ # Build static Cyrus SASL (after OpenSSL and Kerberos since it needs both)
309
+ log "Building static Cyrus SASL $CYRUS_SASL_VERSION with Kerberos support..."
310
+ SASL_PREFIX="$DEPS_PREFIX/static-sasl-$CYRUS_SASL_VERSION"
311
+ SASL_TARBALL="cyrus-sasl-$CYRUS_SASL_VERSION.tar.gz"
312
+ SASL_DIR="cyrus-sasl-$CYRUS_SASL_VERSION"
313
+
314
+ secure_download "$(get_sasl_url)" "$SASL_TARBALL"
315
+ extract_if_needed "$SASL_TARBALL" "$SASL_DIR"
316
+ build_sasl_macos "$ARCH" "$SASL_PREFIX" "$SASL_DIR" "$OPENSSL_PREFIX" "$KRB5_PREFIX"
317
+
318
+ cd "$BUILD_DIR"
319
+
320
+ # Build static ZStd
321
+ log "Building static ZStd $ZSTD_VERSION..."
322
+ ZSTD_PREFIX="$DEPS_PREFIX/static-zstd-$ZSTD_VERSION"
323
+ ZSTD_TARBALL="zstd-$ZSTD_VERSION.tar.gz"
324
+ ZSTD_DIR="zstd-$ZSTD_VERSION"
325
+
326
+ secure_download "$(get_zstd_url)" "$ZSTD_TARBALL"
327
+ extract_if_needed "$ZSTD_TARBALL" "$ZSTD_DIR"
328
+ cd "$ZSTD_DIR"
329
+
330
+ if [ ! -f "$ZSTD_PREFIX/lib/libzstd.a" ]; then
331
+ log "Configuring and building static ZStd..."
332
+ make clean 2>/dev/null || true
333
+
334
+ setup_macos_compiler "$ARCH"
335
+
336
+ # Build static library using ZStd's Makefile
337
+ make lib-mt CFLAGS="$CFLAGS" PREFIX="$ZSTD_PREFIX" -j$(get_cpu_count)
338
+ make install PREFIX="$ZSTD_PREFIX"
339
+
340
+ # Verify the build
341
+ if [ ! -f "$ZSTD_PREFIX/lib/libzstd.a" ]; then
342
+ error "Failed to build static ZStd"
343
+ fi
344
+
345
+ log "✅ Static ZStd built successfully at $ZSTD_PREFIX"
346
+ else
347
+ log "Static ZStd already built, skipping..."
348
+ fi
349
+
350
+ cd "$BUILD_DIR"
351
+
352
+ # Build static zlib
353
+ log "Building static zlib $ZLIB_VERSION..."
354
+ ZLIB_PREFIX="$DEPS_PREFIX/static-zlib-$ZLIB_VERSION"
355
+ ZLIB_TARBALL="zlib-$ZLIB_VERSION.tar.gz"
356
+ ZLIB_DIR="zlib-$ZLIB_VERSION"
357
+
358
+ secure_download "$(get_zlib_url)" "$ZLIB_TARBALL"
359
+ extract_if_needed "$ZLIB_TARBALL" "$ZLIB_DIR"
360
+ build_static_lib_macos "z" "$ARCH" "$ZLIB_PREFIX" "$ZLIB_DIR" "--static"
361
+
362
+ cd "$BUILD_DIR"
363
+
364
+ # Completely disable pkg-config to prevent Homebrew library detection
365
+ log "Disabling pkg-config to prevent Homebrew interference..."
366
+ export PKG_CONFIG=""
367
+ export PKG_CONFIG_PATH=""
368
+ export PKG_CONFIG_LIBDIR=""
369
+
370
+ # Create a dummy pkg-config that always fails
371
+ mkdir -p "$BUILD_DIR/no-pkg-config"
372
+ cat > "$BUILD_DIR/no-pkg-config/pkg-config" << 'EOF'
373
+ #!/bin/sh
374
+ # Dummy pkg-config that always fails to prevent Homebrew detection
375
+ exit 1
376
+ EOF
377
+ chmod +x "$BUILD_DIR/no-pkg-config/pkg-config"
378
+
379
+ # Put our dummy pkg-config first in PATH
380
+ export PATH="$BUILD_DIR/no-pkg-config:$PATH"
381
+
382
+ log "pkg-config disabled - configure will use manual library detection only"
383
+
384
+ # Extract librdkafka
385
+ log "Extracting librdkafka..."
386
+ tar xzf "$LIBRDKAFKA_TARBALL"
387
+ cd "librdkafka-$LIBRDKAFKA_VERSION"
388
+
389
+ # Fix permissions and apply patches
390
+ fix_configure_permissions
391
+ apply_patches PATCHES_FOUND
392
+
393
+ # Set compiler flags for librdkafka
394
+ setup_macos_compiler "$ARCH"
395
+
396
+ # Configure librdkafka with static dependencies INCLUDING Kerberos
397
+ log "Configuring librdkafka with static dependencies including Kerberos..."
398
+
399
+ # Tell configure that math functions don't need -lm on macOS
400
+ export ac_cv_lib_m_floor=yes
401
+ export ac_cv_lib_m_ceil=yes
402
+ export ac_cv_lib_m_sqrt=yes
403
+ export ac_cv_lib_m_pow=yes
404
+ export LIBS="" # Clear any LIBS that might include -lm
405
+
406
+ # Use our static libraries instead of system versions (now including Kerberos)
407
+ export CPPFLAGS="$CPPFLAGS -I$OPENSSL_PREFIX/include -I$SASL_PREFIX/include -I$KRB5_PREFIX/include -I$ZLIB_PREFIX/include -I$ZSTD_PREFIX/include"
408
+ export LDFLAGS="-L$OPENSSL_PREFIX/lib -L$SASL_PREFIX/lib -L$KRB5_PREFIX/lib -L$ZLIB_PREFIX/lib -L$ZSTD_PREFIX/lib"
409
+
410
+ if [ -f configure ]; then
411
+ log "Using mklove configure script"
412
+ ./configure \
413
+ --enable-static \
414
+ --disable-shared \
415
+ --disable-curl \
416
+ --enable-gssapi
417
+ else
418
+ error "No configure script found"
419
+ fi
420
+
421
+ # Fix system library path for linking
422
+ MACOS_SDK_PATH=$(xcrun --show-sdk-path)
423
+ export LDFLAGS="$LDFLAGS -L$MACOS_SDK_PATH/usr/lib"
424
+
425
+ # Build librdkafka
426
+ log "Compiling librdkafka..."
427
+ make clean || true
428
+
429
+ # Build with LIBS override, but ignore dylib build failures
430
+ make -j$(get_cpu_count) LIBS="" || {
431
+ log "Build failed (expected - dylib linking issue), checking if static library was created..."
432
+ }
433
+
434
+ # Verify static library exists (this is what we actually need)
435
+ if [ ! -f src/librdkafka.a ]; then
436
+ error "librdkafka.a not found after build"
437
+ fi
438
+
439
+ log "✅ Static librdkafka.a built successfully"
440
+
441
+ # Remove the dylib check since we're building our own
442
+ # Don't check for src/librdkafka.1.dylib
443
+
444
+ log "librdkafka built successfully - proceeding to create custom self-contained dylib"
445
+
446
+ # Create self-contained dylib with Kerberos libraries included
447
+ log "Creating self-contained librdkafka.dylib with Kerberos support..."
448
+
449
+ # Create self-contained shared library by linking all static dependencies (NOW INCLUDING KERBEROS)
450
+ # This is the macOS equivalent of your Linux gcc -shared command
451
+ clang -dynamiclib -fPIC \
452
+ -Wl,-force_load,src/librdkafka.a \
453
+ -Wl,-force_load,"$SASL_PREFIX/lib/libsasl2.a" \
454
+ -Wl,-force_load,"$KRB5_PREFIX/lib/libgssapi_krb5.a" \
455
+ -Wl,-force_load,"$KRB5_PREFIX/lib/libkrb5.a" \
456
+ -Wl,-force_load,"$KRB5_PREFIX/lib/libk5crypto.a" \
457
+ -Wl,-force_load,"$KRB5_PREFIX/lib/libcom_err.a" \
458
+ -Wl,-force_load,"$KRB5_PREFIX/lib/libkrb5support.a" \
459
+ -Wl,-force_load,"$OPENSSL_PREFIX/lib/libssl.a" \
460
+ -Wl,-force_load,"$OPENSSL_PREFIX/lib/libcrypto.a" \
461
+ -Wl,-force_load,"$ZLIB_PREFIX/lib/libz.a" \
462
+ -Wl,-force_load,"$ZSTD_PREFIX/lib/libzstd.a" \
463
+ -o librdkafka.dylib \
464
+ -lpthread -lc -arch $ARCH -lresolv \
465
+ -install_name @rpath/librdkafka.dylib \
466
+ -Wl,-undefined,dynamic_lookup
467
+
468
+ if [ ! -f librdkafka.dylib ]; then
469
+ error "Failed to create self-contained librdkafka.dylib"
470
+ fi
471
+
472
+ log "✅ Self-contained librdkafka.dylib with Kerberos support created successfully"
473
+
474
+ # Verify the self-contained build
475
+ log "Verifying self-contained build..."
476
+ file librdkafka.dylib
477
+
478
+ log "Checking dependencies with otool (should only show system libraries):"
479
+ otool -L librdkafka.dylib
480
+
481
+ # Check for external dependencies that shouldn't be there (strict like Linux version)
482
+ log "Checking for external dependencies (should only show system libraries):"
483
+ EXTERNAL_DEPS=$(otool -L librdkafka.dylib | grep -v "librdkafka.dylib" | grep -v "/usr/lib/" | grep -v "/System/Library/" | grep -v "@rpath" || true)
484
+ if [ -n "$EXTERNAL_DEPS" ]; then
485
+ error "Found external dependencies - library is not self-contained: $EXTERNAL_DEPS"
486
+ else
487
+ log "✅ No external dependencies found - library is self-contained!"
488
+ fi
489
+
490
+ log "Checking exported symbols:"
491
+ # Avoid SIGPIPE by not using head in a pipe
492
+ nm -gU librdkafka.dylib > /tmp/symbols.txt 2>/dev/null || true
493
+ if [ -f /tmp/symbols.txt ]; then
494
+ head -10 /tmp/symbols.txt
495
+ rm -f /tmp/symbols.txt
496
+ else
497
+ log "Could not extract symbols (this is normal)"
498
+ fi
499
+
500
+ # Force output flush and add small delay
501
+ sync
502
+ sleep 1
503
+
504
+ # Copy to output directory
505
+ OUTPUT_DIR="$SCRIPT_DIR"
506
+ cp librdkafka.dylib "$OUTPUT_DIR/"
507
+ cp src/librdkafka.a "$OUTPUT_DIR/"
508
+
509
+ log "Build artifacts copied to: $OUTPUT_DIR/"
510
+ log " - librdkafka.dylib (shared library)"
511
+ log " - librdkafka.a (static library)"
512
+
513
+ # Force another flush
514
+ sync
515
+ sleep 1
516
+
517
+ # Print summaries
518
+ print_security_summary
519
+
520
+ # Enhanced summary for macOS with Kerberos
521
+ sync
522
+ echo ""
523
+ echo "🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉"
524
+ log "Build completed successfully!"
525
+ log "📦 Self-contained librdkafka built for macOS $ARCH with Kerberos support:"
526
+ log " ✅ Static library: librdkafka.a"
527
+ log " ✅ Self-contained dylib: librdkafka.dylib (with bundled dependencies)"
528
+ log " ✅ Static OpenSSL: $OPENSSL_VERSION (SSL/TLS support) - bundled"
529
+ log " ✅ Static Cyrus SASL: $CYRUS_SASL_VERSION (authentication for AWS MSK) - bundled"
530
+ log " ✅ Static MIT Kerberos: $KRB5_VERSION (GSSAPI/Kerberos authentication) - bundled"
531
+ log " ✅ Static zlib: $ZLIB_VERSION (compression) - bundled"
532
+ log " ✅ Static ZStd: $ZSTD_VERSION (high-performance compression) - bundled"
533
+ log ""
534
+ log "🎯 Ready for deployment on macOS systems"
535
+ log "☁️ Compatible with AWS MSK and other secured Kafka clusters"
536
+ log "🔐 Supply chain security: All dependencies cryptographically verified"
537
+ log "📦 Self-contained: Ready for Ruby FFI distribution"
538
+ log "🔑 Kerberos/GSSAPI support: Full feature parity with Linux build"
539
+ log ""
540
+ log "Location: $OUTPUT_DIR/librdkafka.dylib"
541
+ echo "🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉"
542
+
543
+ # Force final flush
544
+ sync
545
+
546
+ # Cleanup
547
+ cleanup_build_dir "$BUILD_DIR"
548
+
549
+ # Reset environment variables
550
+ unset CFLAGS CXXFLAGS CPPFLAGS LDFLAGS
@@ -9,36 +9,52 @@ Gem::Specification.new do |gem|
9
9
  gem.summary = "The rdkafka gem is a modern Kafka client library for Ruby based on librdkafka. It wraps the production-ready C client using the ffi gem and targets Kafka 1.0+ and Ruby 2.7+."
10
10
  gem.license = 'MIT'
11
11
 
12
- gem.files = `git ls-files`.split($\)
13
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
12
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
13
  gem.name = 'karafka-rdkafka'
16
14
  gem.require_paths = ['lib']
17
15
  gem.version = Rdkafka::VERSION
18
16
  gem.required_ruby_version = '>= 3.1'
19
- gem.extensions = %w(ext/Rakefile)
20
- gem.cert_chain = %w[certs/cert.pem]
21
17
 
22
- if $PROGRAM_NAME.end_with?('gem')
23
- gem.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
18
+ if ENV['RUBY_PLATFORM']
19
+ gem.platform = ENV['RUBY_PLATFORM']
20
+ gem.files = `git ls-files`.split($\)
21
+
22
+ # Do not include the source code for librdkafka as it should be precompiled already per
23
+ # platform. Same applies to any possible patches.
24
+ gem.files = gem.files.reject do |file|
25
+ file.match?(%r{^dist/librdkafka-.*\.tar\.gz$}) ||
26
+ file.match?(%r{^dist/patches/})
27
+ end
28
+
29
+ # Add the compiled extensions that exist (not in git)
30
+ if File.exist?('ext/librdkafka.so')
31
+ gem.files << 'ext/librdkafka.so'
32
+ end
33
+
34
+ if File.exist?('ext/librdkafka.dylib')
35
+ gem.files << 'ext/librdkafka.dylib'
36
+ end
37
+ else
38
+ gem.platform = Gem::Platform::RUBY
39
+ gem.files = `git ls-files`.split($\)
40
+ gem.extensions = %w(ext/Rakefile)
24
41
  end
25
42
 
26
43
  gem.add_dependency 'ffi', '~> 1.15'
44
+ gem.add_dependency 'logger'
27
45
  gem.add_dependency 'mini_portile2', '~> 2.6'
46
+ gem.add_dependency 'ostruct'
28
47
  gem.add_dependency 'rake', '> 12'
29
- gem.add_dependency 'logger', '>= 1.5'
30
48
 
31
49
  gem.add_development_dependency 'pry'
32
50
  gem.add_development_dependency 'rspec', '~> 3.5'
33
51
  gem.add_development_dependency 'rake'
34
52
  gem.add_development_dependency 'simplecov'
35
- gem.add_development_dependency 'guard'
36
- gem.add_development_dependency 'guard-rspec'
37
53
 
38
54
  gem.metadata = {
39
55
  'funding_uri' => 'https://karafka.io/#become-pro',
40
56
  'homepage_uri' => 'https://karafka.io',
41
- 'changelog_uri' => 'https://github.com/karafka/karafka-rdkafka/blob/master/CHANGELOG.md',
57
+ 'changelog_uri' => 'https://karafka.io/docs/Changelog-Karafka-Rdkafka/',
42
58
  'bug_tracker_uri' => 'https://github.com/karafka/karafka-rdkafka/issues',
43
59
  'source_code_uri' => 'https://github.com/karafka/karafka-rdkafka',
44
60
  'documentation_uri' => 'https://karafka.io/docs',
@@ -35,6 +35,8 @@ module Rdkafka
35
35
  RD_KAFKA_OFFSET_STORED = -1000
36
36
  RD_KAFKA_OFFSET_INVALID = -1001
37
37
 
38
+ EMPTY_HASH = {}.freeze
39
+
38
40
  class SizePtr < FFI::Struct
39
41
  layout :value, :size_t
40
42
  end
@@ -215,9 +217,31 @@ module Rdkafka
215
217
  StatsCallback = FFI::Function.new(
216
218
  :int, [:pointer, :string, :int, :pointer]
217
219
  ) do |_client_ptr, json, _json_len, _opaque|
218
- # Pass the stats hash to callback in config
219
220
  if Rdkafka::Config.statistics_callback
220
221
  stats = JSON.parse(json)
222
+
223
+ # If user requested statistics callbacks, we can use the statistics data to get the
224
+ # partitions count for each topic when this data is published. That way we do not have
225
+ # to query this information when user is using `partition_key`. This takes around 0.02ms
226
+ # every statistics interval period (most likely every 5 seconds) and saves us from making
227
+ # any queries to the cluster for the partition count.
228
+ #
229
+ # One edge case is if user would set the `statistics.interval.ms` much higher than the
230
+ # default current partition count refresh (30 seconds). This is taken care of as the lack
231
+ # of reporting to the partitions cache will cause cache expire and blocking refresh.
232
+ #
233
+ # If user sets `topic.metadata.refresh.interval.ms` too high this is on the user.
234
+ #
235
+ # Since this cache is shared, having few consumers and/or producers in one process will
236
+ # automatically improve the querying times even with low refresh times.
237
+ (stats['topics'] || EMPTY_HASH).each do |topic_name, details|
238
+ partitions_count = details['partitions'].keys.reject { |k| k == '-1' }.size
239
+
240
+ next unless partitions_count.positive?
241
+
242
+ Rdkafka::Producer.partitions_count_cache.set(topic_name, partitions_count)
243
+ end
244
+
221
245
  Rdkafka::Config.statistics_callback.call(stats)
222
246
  end
223
247
 
@@ -256,7 +280,7 @@ module Rdkafka
256
280
  OAuthbearerTokenRefreshCallback = FFI::Function.new(
257
281
  :void, [:pointer, :string, :pointer]
258
282
  ) do |client_ptr, config, _opaque|
259
- if Rdkafka::Config.oauthbearer_token_refresh_callback
283
+ if Rdkafka::Config.oauthbearer_token_refresh_callback && !client_ptr.null?
260
284
  Rdkafka::Config.oauthbearer_token_refresh_callback.call(config, Rdkafka::Bindings.rd_kafka_name(client_ptr))
261
285
  end
262
286
  end
@@ -382,12 +406,15 @@ module Rdkafka
382
406
  def self.partitioner(str, partition_count, partitioner_name = "consistent_random")
383
407
  # Return RD_KAFKA_PARTITION_UA(unassigned partition) when partition count is nil/zero.
384
408
  return -1 unless partition_count&.nonzero?
409
+ # musl architecture crashes with empty string
410
+ return 0 if str.empty?
385
411
 
386
- str_ptr = str.empty? ? FFI::MemoryPointer::NULL : FFI::MemoryPointer.from_string(str)
412
+ str_ptr = FFI::MemoryPointer.from_string(str)
387
413
  method_name = PARTITIONERS.fetch(partitioner_name) do
388
414
  raise Rdkafka::Config::ConfigError.new("Unknown partitioner: #{partitioner_name}")
389
415
  end
390
- public_send(method_name, nil, str_ptr, str.size > 0 ? str.size : 1, partition_count, nil, nil)
416
+
417
+ public_send(method_name, nil, str_ptr, str.size, partition_count, nil, nil)
391
418
  end
392
419
 
393
420
  # Create Topics
@@ -129,7 +129,10 @@ module Rdkafka
129
129
  end
130
130
 
131
131
  # Default config that can be overwritten.
132
- DEFAULT_CONFIG = {}.freeze
132
+ DEFAULT_CONFIG = {
133
+ # Request api version so advanced features work
134
+ :"api.version.request" => true
135
+ }.freeze
133
136
 
134
137
  # Required config that cannot be overwritten.
135
138
  REQUIRED_CONFIG = {