karafka-rdkafka 0.20.0.rc5-arm64-darwin → 0.21.0.rc1-arm64-darwin

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.
@@ -263,8 +263,6 @@ describe Rdkafka::Producer do
263
263
  expect(message.partition).to eq 1
264
264
  expect(message.payload).to eq "payload"
265
265
  expect(message.key).to eq "key"
266
- # Since api.version.request is on by default we will get
267
- # the message creation timestamp if it's not set.
268
266
  expect(message.timestamp).to be_within(10).of(Time.now)
269
267
  end
270
268
 
@@ -340,7 +338,7 @@ describe Rdkafka::Producer do
340
338
  )
341
339
  end
342
340
 
343
- expect(messages[0].partition).to eq 0
341
+ expect(messages[0].partition).to be >= 0
344
342
  expect(messages[0].key).to eq 'a'
345
343
  end
346
344
 
@@ -1231,4 +1229,298 @@ describe Rdkafka::Producer do
1231
1229
  end
1232
1230
  end
1233
1231
  end
1232
+
1233
+ let(:producer) { rdkafka_producer_config.producer }
1234
+ let(:all_partitioners) { %w(random consistent consistent_random murmur2 murmur2_random fnv1a fnv1a_random) }
1235
+
1236
+ describe "partitioner behavior through producer API" do
1237
+ context "testing all partitioners with same key" do
1238
+ it "should not return partition 0 for all partitioners" do
1239
+ test_key = "test-key-123"
1240
+ results = {}
1241
+
1242
+ all_partitioners.each do |partitioner|
1243
+ handle = producer.produce(
1244
+ topic: "partitioner_test_topic",
1245
+ payload: "test payload",
1246
+ partition_key: test_key,
1247
+ partitioner: partitioner
1248
+ )
1249
+
1250
+ report = handle.wait(max_wait_timeout: 5)
1251
+ results[partitioner] = report.partition
1252
+ end
1253
+
1254
+ # Should not all be the same partition (especially not all 0)
1255
+ unique_partitions = results.values.uniq
1256
+ expect(unique_partitions.size).to be > 1
1257
+ end
1258
+ end
1259
+
1260
+ context "empty string partition key" do
1261
+ it "should produce message with empty partition key without crashing and go to partition 0 for all partitioners" do
1262
+ all_partitioners.each do |partitioner|
1263
+ handle = producer.produce(
1264
+ topic: "partitioner_test_topic",
1265
+ payload: "test payload",
1266
+ key: "test-key",
1267
+ partition_key: "",
1268
+ partitioner: partitioner
1269
+ )
1270
+
1271
+ report = handle.wait(max_wait_timeout: 5)
1272
+ expect(report.partition).to be >= 0
1273
+ end
1274
+ end
1275
+ end
1276
+
1277
+ context "nil partition key" do
1278
+ it "should handle nil partition key gracefully" do
1279
+ handle = producer.produce(
1280
+ topic: "partitioner_test_topic",
1281
+ payload: "test payload",
1282
+ key: "test-key",
1283
+ partition_key: nil
1284
+ )
1285
+
1286
+ report = handle.wait(max_wait_timeout: 5)
1287
+ expect(report.partition).to be >= 0
1288
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1289
+ end
1290
+ end
1291
+
1292
+ context "various key types and lengths with different partitioners" do
1293
+ it "should handle very short keys with all partitioners" do
1294
+ all_partitioners.each do |partitioner|
1295
+ handle = producer.produce(
1296
+ topic: "partitioner_test_topic",
1297
+ payload: "test payload",
1298
+ partition_key: "a",
1299
+ partitioner: partitioner
1300
+ )
1301
+
1302
+ report = handle.wait(max_wait_timeout: 5)
1303
+ expect(report.partition).to be >= 0
1304
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1305
+ end
1306
+ end
1307
+
1308
+ it "should handle very long keys with all partitioners" do
1309
+ long_key = "a" * 1000
1310
+
1311
+ all_partitioners.each do |partitioner|
1312
+ handle = producer.produce(
1313
+ topic: "partitioner_test_topic",
1314
+ payload: "test payload",
1315
+ partition_key: long_key,
1316
+ partitioner: partitioner
1317
+ )
1318
+
1319
+ report = handle.wait(max_wait_timeout: 5)
1320
+ expect(report.partition).to be >= 0
1321
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1322
+ end
1323
+ end
1324
+
1325
+ it "should handle unicode keys with all partitioners" do
1326
+ unicode_key = "测试键值🚀"
1327
+
1328
+ all_partitioners.each do |partitioner|
1329
+ handle = producer.produce(
1330
+ topic: "partitioner_test_topic",
1331
+ payload: "test payload",
1332
+ partition_key: unicode_key,
1333
+ partitioner: partitioner
1334
+ )
1335
+
1336
+ report = handle.wait(max_wait_timeout: 5)
1337
+ expect(report.partition).to be >= 0
1338
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1339
+ end
1340
+ end
1341
+ end
1342
+
1343
+ context "consistency testing for deterministic partitioners" do
1344
+ %w(consistent murmur2 fnv1a).each do |partitioner|
1345
+ it "should consistently route same partition key to same partition with #{partitioner}" do
1346
+ partition_key = "consistent-test-key"
1347
+
1348
+ # Produce multiple messages with same partition key
1349
+ reports = 5.times.map do
1350
+ handle = producer.produce(
1351
+ topic: "partitioner_test_topic",
1352
+ payload: "test payload #{Time.now.to_f}",
1353
+ partition_key: partition_key,
1354
+ partitioner: partitioner
1355
+ )
1356
+ handle.wait(max_wait_timeout: 5)
1357
+ end
1358
+
1359
+ # All should go to same partition
1360
+ partitions = reports.map(&:partition).uniq
1361
+ expect(partitions.size).to eq(1)
1362
+ end
1363
+ end
1364
+ end
1365
+
1366
+ context "randomness testing for random partitioners" do
1367
+ %w(random consistent_random murmur2_random fnv1a_random).each do |partitioner|
1368
+ it "should potentially distribute across partitions with #{partitioner}" do
1369
+ # Note: random partitioners might still return same value by chance
1370
+ partition_key = "random-test-key"
1371
+
1372
+ reports = 10.times.map do
1373
+ handle = producer.produce(
1374
+ topic: "partitioner_test_topic",
1375
+ payload: "test payload #{Time.now.to_f}",
1376
+ partition_key: partition_key,
1377
+ partitioner: partitioner
1378
+ )
1379
+ handle.wait(max_wait_timeout: 5)
1380
+ end
1381
+
1382
+ partitions = reports.map(&:partition)
1383
+
1384
+ # Just ensure they're valid partitions
1385
+ partitions.each do |partition|
1386
+ expect(partition).to be >= 0
1387
+ expect(partition).to be < producer.partition_count("partitioner_test_topic")
1388
+ end
1389
+ end
1390
+ end
1391
+ end
1392
+
1393
+ context "comparing different partitioners with same key" do
1394
+ it "should route different partition keys to potentially different partitions" do
1395
+ keys = ["key1", "key2", "key3", "key4", "key5"]
1396
+
1397
+ all_partitioners.each do |partitioner|
1398
+ reports = keys.map do |key|
1399
+ handle = producer.produce(
1400
+ topic: "partitioner_test_topic",
1401
+ payload: "test payload",
1402
+ partition_key: key,
1403
+ partitioner: partitioner
1404
+ )
1405
+ handle.wait(max_wait_timeout: 5)
1406
+ end
1407
+
1408
+ partitions = reports.map(&:partition).uniq
1409
+
1410
+ # Should distribute across multiple partitions for most partitioners
1411
+ # (though some might hash all keys to same partition by chance)
1412
+ expect(partitions.all? { |p| p >= 0 && p < producer.partition_count("partitioner_test_topic") }).to be true
1413
+ end
1414
+ end
1415
+ end
1416
+
1417
+ context "partition key vs regular key behavior" do
1418
+ it "should use partition key for partitioning when both key and partition_key are provided" do
1419
+ # Use keys that would hash to different partitions
1420
+ regular_key = "regular-key-123"
1421
+ partition_key = "partition-key-456"
1422
+
1423
+ # Message with both keys
1424
+ handle1 = producer.produce(
1425
+ topic: "partitioner_test_topic",
1426
+ payload: "test payload 1",
1427
+ key: regular_key,
1428
+ partition_key: partition_key
1429
+ )
1430
+
1431
+ # Message with only partition key (should go to same partition)
1432
+ handle2 = producer.produce(
1433
+ topic: "partitioner_test_topic",
1434
+ payload: "test payload 2",
1435
+ partition_key: partition_key
1436
+ )
1437
+
1438
+ # Message with only regular key (should go to different partition)
1439
+ handle3 = producer.produce(
1440
+ topic: "partitioner_test_topic",
1441
+ payload: "test payload 3",
1442
+ key: regular_key
1443
+ )
1444
+
1445
+ report1 = handle1.wait(max_wait_timeout: 5)
1446
+ report2 = handle2.wait(max_wait_timeout: 5)
1447
+ report3 = handle3.wait(max_wait_timeout: 5)
1448
+
1449
+ # Messages 1 and 2 should go to same partition (both use partition_key)
1450
+ expect(report1.partition).to eq(report2.partition)
1451
+
1452
+ # Message 3 should potentially go to different partition (uses regular key)
1453
+ expect(report3.partition).not_to eq(report1.partition)
1454
+ end
1455
+ end
1456
+
1457
+ context "edge case combinations with different partitioners" do
1458
+ it "should handle nil partition key with all partitioners" do
1459
+ all_partitioners.each do |partitioner|
1460
+ handle = producer.produce(
1461
+ topic: "partitioner_test_topic",
1462
+ payload: "test payload",
1463
+ key: "test-key",
1464
+ partition_key: nil,
1465
+ partitioner: partitioner
1466
+ )
1467
+
1468
+ report = handle.wait(max_wait_timeout: 5)
1469
+ expect(report.partition).to be >= 0
1470
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1471
+ end
1472
+ end
1473
+
1474
+ it "should handle whitespace-only partition key with all partitioners" do
1475
+ all_partitioners.each do |partitioner|
1476
+ handle = producer.produce(
1477
+ topic: "partitioner_test_topic",
1478
+ payload: "test payload",
1479
+ partition_key: " ",
1480
+ partitioner: partitioner
1481
+ )
1482
+
1483
+ report = handle.wait(max_wait_timeout: 5)
1484
+ expect(report.partition).to be >= 0
1485
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1486
+ end
1487
+ end
1488
+
1489
+ it "should handle newline characters in partition key with all partitioners" do
1490
+ all_partitioners.each do |partitioner|
1491
+ handle = producer.produce(
1492
+ topic: "partitioner_test_topic",
1493
+ payload: "test payload",
1494
+ partition_key: "key\nwith\nnewlines",
1495
+ partitioner: partitioner
1496
+ )
1497
+
1498
+ report = handle.wait(max_wait_timeout: 5)
1499
+ expect(report.partition).to be >= 0
1500
+ expect(report.partition).to be < producer.partition_count("partitioner_test_topic")
1501
+ end
1502
+ end
1503
+ end
1504
+
1505
+ context "debugging partitioner issues" do
1506
+ it "should show if all partitioners return 0 (indicating a problem)" do
1507
+ test_key = "debug-test-key"
1508
+ zero_count = 0
1509
+
1510
+ all_partitioners.each do |partitioner|
1511
+ handle = producer.produce(
1512
+ topic: "partitioner_test_topic",
1513
+ payload: "debug payload",
1514
+ partition_key: test_key,
1515
+ partitioner: partitioner
1516
+ )
1517
+
1518
+ report = handle.wait(max_wait_timeout: 5)
1519
+ zero_count += 1 if report.partition == 0
1520
+ end
1521
+
1522
+ expect(zero_count).to be < all_partitioners.size
1523
+ end
1524
+ end
1525
+ end
1234
1526
  end
data/spec/spec_helper.rb CHANGED
@@ -15,9 +15,7 @@ require "securerandom"
15
15
 
16
16
  def rdkafka_base_config
17
17
  {
18
- :"api.version.request" => false,
19
- :"broker.version.fallback" => "1.0",
20
- :"bootstrap.servers" => "127.0.0.1:9092",
18
+ :"bootstrap.servers" => "localhost:9092",
21
19
  # Display statistics and refresh often just to cover those in specs
22
20
  :'statistics.interval.ms' => 1_000,
23
21
  :'topic.metadata.refresh.interval.ms' => 1_000
@@ -78,18 +76,32 @@ end
78
76
 
79
77
  def wait_for_message(topic:, delivery_report:, timeout_in_seconds: 30, consumer: nil)
80
78
  new_consumer = consumer.nil?
81
- consumer ||= rdkafka_consumer_config.consumer
79
+ consumer ||= rdkafka_consumer_config('allow.auto.create.topics': true).consumer
82
80
  consumer.subscribe(topic)
83
81
  timeout = Time.now.to_i + timeout_in_seconds
82
+ retry_count = 0
83
+ max_retries = 10
84
+
84
85
  loop do
85
86
  if timeout <= Time.now.to_i
86
87
  raise "Timeout of #{timeout_in_seconds} seconds reached in wait_for_message"
87
88
  end
88
- message = consumer.poll(100)
89
- if message &&
90
- message.partition == delivery_report.partition &&
91
- message.offset == delivery_report.offset
92
- return message
89
+
90
+ begin
91
+ message = consumer.poll(100)
92
+ if message &&
93
+ message.partition == delivery_report.partition &&
94
+ message.offset == delivery_report.offset
95
+ return message
96
+ end
97
+ rescue Rdkafka::RdkafkaError => e
98
+ if e.code == :unknown_topic_or_part && retry_count < max_retries
99
+ retry_count += 1
100
+ sleep(0.1) # Small delay before retry
101
+ next
102
+ else
103
+ raise
104
+ end
93
105
  end
94
106
  end
95
107
  ensure
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka-rdkafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0.rc5
4
+ version: 0.21.0.rc1
5
5
  platform: arm64-darwin
6
6
  authors:
7
7
  - Thijs Cadier
@@ -53,33 +53,33 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.6'
55
55
  - !ruby/object:Gem::Dependency
56
- name: ostruct
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - ">"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '12'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - ">"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '12'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rake
70
+ name: ostruct
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">"
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: '12'
76
- type: :runtime
75
+ version: '0'
76
+ type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">"
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: '12'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: pry
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -241,7 +241,6 @@ files:
241
241
  - spec/rdkafka/producer/delivery_handle_spec.rb
242
242
  - spec/rdkafka/producer/delivery_report_spec.rb
243
243
  - spec/rdkafka/producer/partitions_count_cache_spec.rb
244
- - spec/rdkafka/producer/partitions_count_spec.rb
245
244
  - spec/rdkafka/producer_spec.rb
246
245
  - spec/spec_helper.rb
247
246
  licenses: