@platformatic/rdkafka 4.0.1 → 4.1.0
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.
- package/README.md +5 -5
- package/bench/seed.sh +0 -0
- package/binding.gyp +8 -1
- package/ci/prepublish.js +0 -1
- package/config.d.ts +126 -10
- package/configure +0 -0
- package/deps/librdkafka/.semaphore/run-all-tests.yml +77 -0
- package/deps/librdkafka/.semaphore/semaphore-integration.yml +250 -0
- package/deps/librdkafka/.semaphore/semaphore.yml +67 -44
- package/deps/librdkafka/.semaphore/verify-linux-packages.yml +41 -0
- package/deps/librdkafka/CHANGELOG.md +471 -1
- package/deps/librdkafka/CONFIGURATION.md +29 -9
- package/deps/librdkafka/INTRODUCTION.md +909 -601
- package/deps/librdkafka/LICENSES.txt +592 -1
- package/deps/librdkafka/Makefile +2 -1
- package/deps/librdkafka/_mkltmpuuWEwa.c +13 -0
- package/deps/librdkafka/configure +0 -0
- package/deps/librdkafka/debian/rules +0 -0
- package/deps/librdkafka/dev-conf.sh +0 -0
- package/deps/librdkafka/examples/consumer.c +18 -8
- package/deps/librdkafka/examples/describe_consumer_groups.c +25 -6
- package/deps/librdkafka/examples/incremental_alter_configs.c +4 -5
- package/deps/librdkafka/examples/kafkatest_verifiable_client.cpp +26 -42
- package/deps/librdkafka/examples/openssl_engine_example.cpp +3 -4
- package/deps/librdkafka/examples/producer.cpp +2 -2
- package/deps/librdkafka/examples/user_scram.c +4 -5
- package/deps/librdkafka/examples/win_ssl_cert_store.cpp +4 -3
- package/deps/librdkafka/lds-gen.py +0 -0
- package/deps/librdkafka/mklove/Makefile.base +0 -0
- package/deps/librdkafka/packaging/RELEASE.md +8 -0
- package/deps/librdkafka/packaging/alpine/build-alpine.sh +0 -0
- package/deps/librdkafka/packaging/cp/README.md +4 -1
- package/deps/librdkafka/packaging/cp/check_features.c +12 -4
- package/deps/librdkafka/packaging/cp/verify-deb.sh +6 -6
- package/deps/librdkafka/packaging/cp/verify-packages.sh +52 -31
- package/deps/librdkafka/packaging/cp/verify-rpm.sh +6 -13
- package/deps/librdkafka/packaging/debian/rules +0 -0
- package/deps/librdkafka/packaging/get_version.py +0 -0
- package/deps/librdkafka/packaging/homebrew/brew-update-pr.sh +0 -0
- package/deps/librdkafka/packaging/mingw-w64/{travis-before-install.sh → export-variables.sh} +0 -7
- package/deps/librdkafka/packaging/mingw-w64/semaphoreci-build.sh +1 -1
- package/deps/librdkafka/packaging/nuget/artifact.py +0 -0
- package/deps/librdkafka/packaging/nuget/cleanup-s3.py +0 -0
- package/deps/librdkafka/packaging/nuget/nuget.sh +0 -0
- package/deps/librdkafka/packaging/nuget/packaging.py +0 -0
- package/deps/librdkafka/packaging/nuget/push-to-nuget.sh +0 -0
- package/deps/librdkafka/packaging/nuget/release.py +0 -0
- package/deps/librdkafka/packaging/nuget/requirements.txt +3 -3
- package/deps/librdkafka/packaging/rpm/mock-on-docker.sh +0 -0
- package/deps/librdkafka/packaging/rpm/tests/run-test.sh +0 -0
- package/deps/librdkafka/packaging/rpm/tests/test-on-docker.sh +0 -0
- package/deps/librdkafka/packaging/tools/build-configurations-checks.sh +3 -1
- package/deps/librdkafka/packaging/tools/build-deb-package.sh +0 -0
- package/deps/librdkafka/packaging/tools/build-debian.sh +0 -0
- package/deps/librdkafka/packaging/tools/build-manylinux.sh +0 -0
- package/deps/librdkafka/packaging/tools/build-release-artifacts.sh +0 -0
- package/deps/librdkafka/packaging/tools/distro-build.sh +0 -0
- package/deps/librdkafka/packaging/tools/gh-release-checksums.py +0 -0
- package/deps/librdkafka/packaging/tools/rdutcoverage.sh +0 -0
- package/deps/librdkafka/packaging/tools/run-in-docker.sh +28 -0
- package/deps/librdkafka/packaging/tools/run-integration-tests.sh +31 -0
- package/deps/librdkafka/packaging/tools/run-style-check.sh +4 -0
- package/deps/librdkafka/packaging/tools/style-format.sh +6 -5
- package/deps/librdkafka/packaging/tools/update_rpcs_max_versions.py +100 -0
- package/deps/librdkafka/service.yml +154 -0
- package/deps/librdkafka/src/generate_proto.sh +0 -0
- package/deps/librdkafka/src/rd.h +145 -2
- package/deps/librdkafka/src/rdaddr.h +6 -7
- package/deps/librdkafka/src/rdatomic.h +15 -11
- package/deps/librdkafka/src/rdavg.h +3 -2
- package/deps/librdkafka/src/rdbase64.c +33 -2
- package/deps/librdkafka/src/rdbase64.h +2 -0
- package/deps/librdkafka/src/rdendian.h +10 -9
- package/deps/librdkafka/src/rdhttp.c +418 -100
- package/deps/librdkafka/src/rdhttp.h +23 -5
- package/deps/librdkafka/src/rdkafka.c +299 -145
- package/deps/librdkafka/src/rdkafka.h +89 -6
- package/deps/librdkafka/src/rdkafka_admin.c +482 -105
- package/deps/librdkafka/src/rdkafka_admin.h +42 -0
- package/deps/librdkafka/src/rdkafka_broker.c +463 -312
- package/deps/librdkafka/src/rdkafka_broker.h +68 -10
- package/deps/librdkafka/src/rdkafka_buf.c +8 -5
- package/deps/librdkafka/src/rdkafka_buf.h +4 -3
- package/deps/librdkafka/src/rdkafka_cert.c +25 -1
- package/deps/librdkafka/src/rdkafka_cert.h +4 -3
- package/deps/librdkafka/src/rdkafka_cgrp.c +468 -143
- package/deps/librdkafka/src/rdkafka_cgrp.h +36 -2
- package/deps/librdkafka/src/rdkafka_conf.c +526 -102
- package/deps/librdkafka/src/rdkafka_conf.h +68 -4
- package/deps/librdkafka/src/rdkafka_feature.c +472 -35
- package/deps/librdkafka/src/rdkafka_feature.h +2 -0
- package/deps/librdkafka/src/rdkafka_fetcher.c +22 -2
- package/deps/librdkafka/src/rdkafka_fetcher.h +2 -2
- package/deps/librdkafka/src/rdkafka_int.h +84 -46
- package/deps/librdkafka/src/rdkafka_metadata.c +221 -136
- package/deps/librdkafka/src/rdkafka_metadata.h +15 -11
- package/deps/librdkafka/src/rdkafka_metadata_cache.c +279 -73
- package/deps/librdkafka/src/rdkafka_mock.c +795 -19
- package/deps/librdkafka/src/rdkafka_mock.h +119 -0
- package/deps/librdkafka/src/rdkafka_mock_cgrp.c +1298 -132
- package/deps/librdkafka/src/rdkafka_mock_handlers.c +345 -49
- package/deps/librdkafka/src/rdkafka_mock_int.h +180 -61
- package/deps/librdkafka/src/rdkafka_msg.c +35 -19
- package/deps/librdkafka/src/rdkafka_msg.h +3 -3
- package/deps/librdkafka/src/rdkafka_msgset_writer.c +14 -10
- package/deps/librdkafka/src/rdkafka_offset.c +33 -5
- package/deps/librdkafka/src/rdkafka_op.c +1 -1
- package/deps/librdkafka/src/rdkafka_op.h +45 -21
- package/deps/librdkafka/src/rdkafka_partition.c +118 -23
- package/deps/librdkafka/src/rdkafka_partition.h +16 -7
- package/deps/librdkafka/src/rdkafka_proto.h +4 -1
- package/deps/librdkafka/src/rdkafka_protocol.h +1 -0
- package/deps/librdkafka/src/rdkafka_queue.c +125 -48
- package/deps/librdkafka/src/rdkafka_queue.h +19 -0
- package/deps/librdkafka/src/rdkafka_range_assignor.c +1 -1
- package/deps/librdkafka/src/rdkafka_request.c +336 -64
- package/deps/librdkafka/src/rdkafka_request.h +24 -1
- package/deps/librdkafka/src/rdkafka_sasl.c +5 -3
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer.c +14 -10
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.c +1166 -89
- package/deps/librdkafka/src/rdkafka_sasl_oauthbearer_oidc.h +13 -3
- package/deps/librdkafka/src/rdkafka_sasl_plain.c +3 -3
- package/deps/librdkafka/src/rdkafka_sasl_scram.c +4 -3
- package/deps/librdkafka/src/rdkafka_ssl.c +306 -81
- package/deps/librdkafka/src/rdkafka_ssl.h +22 -0
- package/deps/librdkafka/src/rdkafka_sticky_assignor.c +4 -0
- package/deps/librdkafka/src/rdkafka_telemetry.c +80 -23
- package/deps/librdkafka/src/rdkafka_telemetry_encode.c +5 -1
- package/deps/librdkafka/src/rdkafka_telemetry_encode.h +20 -20
- package/deps/librdkafka/src/rdkafka_topic.c +220 -137
- package/deps/librdkafka/src/rdkafka_topic.h +14 -8
- package/deps/librdkafka/src/rdkafka_transport.c +59 -47
- package/deps/librdkafka/src/rdkafka_transport.h +5 -0
- package/deps/librdkafka/src/rdkafka_txnmgr.c +6 -1
- package/deps/librdkafka/src/rdmap.c +6 -1
- package/deps/librdkafka/src/rdrand.c +11 -1
- package/deps/librdkafka/src/rdtime.h +48 -17
- package/deps/librdkafka/src/rdunittest.c +42 -25
- package/deps/librdkafka/src/rdunittest.h +2 -0
- package/deps/librdkafka/src/rdwin32.h +1 -1
- package/deps/librdkafka/src/regexp.c +47 -42
- package/deps/librdkafka/src/tinycthread_extra.c +45 -21
- package/deps/librdkafka/src/tinycthread_extra.h +7 -3
- package/deps/librdkafka/src-cpp/rdkafkacpp.h +28 -2
- package/deps/librdkafka/tests/0001-multiobj.c +4 -0
- package/deps/librdkafka/tests/0004-conf.c +103 -36
- package/deps/librdkafka/tests/0009-mock_cluster.c +1 -0
- package/deps/librdkafka/tests/0011-produce_batch.c +2 -1
- package/deps/librdkafka/tests/0016-client_swname.c +18 -3
- package/deps/librdkafka/tests/0017-compression.c +6 -8
- package/deps/librdkafka/tests/0018-cgrp_term.c +8 -3
- package/deps/librdkafka/tests/0021-rkt_destroy.c +1 -0
- package/deps/librdkafka/tests/0022-consume_batch.c +4 -1
- package/deps/librdkafka/tests/0026-consume_pause.c +12 -14
- package/deps/librdkafka/tests/0028-long_topicnames.c +1 -1
- package/deps/librdkafka/tests/0029-assign_offset.c +6 -8
- package/deps/librdkafka/tests/0030-offset_commit.c +2 -2
- package/deps/librdkafka/tests/0033-regex_subscribe.c +33 -17
- package/deps/librdkafka/tests/0034-offset_reset.c +25 -4
- package/deps/librdkafka/tests/0036-partial_fetch.c +1 -0
- package/deps/librdkafka/tests/0038-performance.c +1 -0
- package/deps/librdkafka/tests/0040-io_event.c +11 -5
- package/deps/librdkafka/tests/0041-fetch_max_bytes.c +1 -0
- package/deps/librdkafka/tests/0044-partition_cnt.c +3 -2
- package/deps/librdkafka/tests/0045-subscribe_update.c +284 -20
- package/deps/librdkafka/tests/0047-partial_buf_tmout.c +1 -0
- package/deps/librdkafka/tests/0048-partitioner.c +1 -1
- package/deps/librdkafka/tests/0050-subscribe_adds.c +1 -0
- package/deps/librdkafka/tests/0051-assign_adds.c +1 -0
- package/deps/librdkafka/tests/0052-msg_timestamps.c +22 -4
- package/deps/librdkafka/tests/0053-stats_cb.cpp +12 -20
- package/deps/librdkafka/tests/0055-producer_latency.c +176 -3
- package/deps/librdkafka/tests/0056-balanced_group_mt.c +6 -2
- package/deps/librdkafka/tests/0058-log.cpp +3 -3
- package/deps/librdkafka/tests/0059-bsearch.cpp +8 -4
- package/deps/librdkafka/tests/0061-consumer_lag.cpp +25 -5
- package/deps/librdkafka/tests/0067-empty_topic.cpp +3 -0
- package/deps/librdkafka/tests/0068-produce_timeout.c +21 -23
- package/deps/librdkafka/tests/0069-consumer_add_parts.c +3 -7
- package/deps/librdkafka/tests/0075-retry.c +38 -1
- package/deps/librdkafka/tests/0076-produce_retry.c +2 -0
- package/deps/librdkafka/tests/0077-compaction.c +8 -2
- package/deps/librdkafka/tests/0080-admin_ut.c +34 -4
- package/deps/librdkafka/tests/0081-admin.c +455 -155
- package/deps/librdkafka/tests/0082-fetch_max_bytes.cpp +4 -0
- package/deps/librdkafka/tests/0083-cb_event.c +8 -3
- package/deps/librdkafka/tests/0084-destroy_flags.c +14 -18
- package/deps/librdkafka/tests/0085-headers.cpp +4 -0
- package/deps/librdkafka/tests/0086-purge.c +40 -7
- package/deps/librdkafka/tests/0088-produce_metadata_timeout.c +1 -1
- package/deps/librdkafka/tests/0089-max_poll_interval.c +12 -7
- package/deps/librdkafka/tests/0090-idempotence.c +1 -2
- package/deps/librdkafka/tests/0091-max_poll_interval_timeout.c +5 -7
- package/deps/librdkafka/tests/0092-mixed_msgver.c +6 -0
- package/deps/librdkafka/tests/0093-holb.c +8 -5
- package/deps/librdkafka/tests/0094-idempotence_msg_timeout.c +1 -0
- package/deps/librdkafka/tests/0097-ssl_verify.cpp +262 -70
- package/deps/librdkafka/tests/0098-consumer-txn.cpp +16 -16
- package/deps/librdkafka/tests/0099-commit_metadata.c +1 -1
- package/deps/librdkafka/tests/0101-fetch-from-follower.cpp +1 -1
- package/deps/librdkafka/tests/0102-static_group_rebalance.c +317 -16
- package/deps/librdkafka/tests/0103-transactions.c +7 -7
- package/deps/librdkafka/tests/0104-fetch_from_follower_mock.c +1 -0
- package/deps/librdkafka/tests/0105-transactions_mock.c +18 -11
- package/deps/librdkafka/tests/0106-cgrp_sess_timeout.c +35 -14
- package/deps/librdkafka/tests/0107-topic_recreate.c +2 -2
- package/deps/librdkafka/tests/0109-auto_create_topics.cpp +86 -26
- package/deps/librdkafka/tests/0110-batch_size.cpp +1 -2
- package/deps/librdkafka/tests/0112-assign_unknown_part.c +3 -14
- package/deps/librdkafka/tests/0113-cooperative_rebalance.cpp +248 -104
- package/deps/librdkafka/tests/0114-sticky_partitioning.cpp +3 -3
- package/deps/librdkafka/tests/0115-producer_auth.cpp +4 -1
- package/deps/librdkafka/tests/0116-kafkaconsumer_close.cpp +7 -5
- package/deps/librdkafka/tests/0117-mock_errors.c +15 -5
- package/deps/librdkafka/tests/0118-commit_rebalance.c +41 -8
- package/deps/librdkafka/tests/0119-consumer_auth.cpp +20 -1
- package/deps/librdkafka/tests/0120-asymmetric_subscription.c +8 -3
- package/deps/librdkafka/tests/0125-immediate_flush.c +1 -1
- package/deps/librdkafka/tests/0126-oauthbearer_oidc.c +323 -8
- package/deps/librdkafka/tests/0129-fetch_aborted_msgs.c +1 -0
- package/deps/librdkafka/tests/0132-strategy_ordering.c +12 -4
- package/deps/librdkafka/tests/0133-ssl_keys.c +28 -6
- package/deps/librdkafka/tests/0137-barrier_batch_consume.c +24 -14
- package/deps/librdkafka/tests/0139-offset_validation_mock.c +510 -2
- package/deps/librdkafka/tests/0140-commit_metadata.cpp +1 -1
- package/deps/librdkafka/tests/0142-reauthentication.c +37 -17
- package/deps/librdkafka/tests/0143-exponential_backoff_mock.c +39 -40
- package/deps/librdkafka/tests/0146-metadata_mock.c +76 -15
- package/deps/librdkafka/tests/0147-consumer_group_consumer_mock.c +952 -0
- package/deps/librdkafka/tests/0148-offset_fetch_commit_error_mock.c +563 -0
- package/deps/librdkafka/tests/0149-broker-same-host-port.c +140 -0
- package/deps/librdkafka/tests/0150-telemetry_mock.c +9 -6
- package/deps/librdkafka/tests/0151-purge-brokers.c +566 -0
- package/deps/librdkafka/tests/0152-rebootstrap.c +59 -0
- package/deps/librdkafka/tests/0153-memberid.c +128 -0
- package/deps/librdkafka/tests/CMakeLists.txt +6 -0
- package/deps/librdkafka/tests/LibrdkafkaTestApp.py +34 -9
- package/deps/librdkafka/tests/autotest.sh +0 -0
- package/deps/librdkafka/tests/broker_version_tests.py +0 -0
- package/deps/librdkafka/tests/buildbox.sh +0 -0
- package/deps/librdkafka/tests/cleanup-checker-tests.sh +0 -0
- package/deps/librdkafka/tests/cluster_testing.py +0 -0
- package/deps/librdkafka/tests/delete-test-topics.sh +0 -0
- package/deps/librdkafka/tests/fixtures/oauthbearer/jwt_assertion_template.json +10 -0
- package/deps/librdkafka/tests/fixtures/ssl/client.keystore.intermediate.p12 +0 -0
- package/deps/librdkafka/tests/fixtures/ssl/client.keystore.p12 +0 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.intermediate.pem +72 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.certificate.pem +42 -101
- package/deps/librdkafka/tests/fixtures/ssl/client2.intermediate.key +46 -0
- package/deps/librdkafka/tests/fixtures/ssl/client2.key +41 -29
- package/deps/librdkafka/tests/fixtures/ssl/create_keys.sh +112 -37
- package/deps/librdkafka/tests/gen-ssl-certs.sh +0 -0
- package/deps/librdkafka/tests/interactive_broker_version.py +0 -0
- package/deps/librdkafka/tests/java/Makefile +2 -1
- package/deps/librdkafka/tests/java/run-class.sh +0 -0
- package/deps/librdkafka/tests/lz4_manual_test.sh +0 -0
- package/deps/librdkafka/tests/multi-broker-version-test.sh +0 -0
- package/deps/librdkafka/tests/parse-refcnt.sh +0 -0
- package/deps/librdkafka/tests/performance_plot.py +0 -0
- package/deps/librdkafka/tests/requirements.txt +1 -1
- package/deps/librdkafka/tests/run-all-tests.sh +79 -0
- package/deps/librdkafka/tests/run-consumer-tests.sh +0 -0
- package/deps/librdkafka/tests/run-producer-tests.sh +0 -0
- package/deps/librdkafka/tests/run-test-batches.py +157 -0
- package/deps/librdkafka/tests/run-test.sh +0 -0
- package/deps/librdkafka/tests/sasl_test.py +0 -0
- package/deps/librdkafka/tests/test.c +478 -18
- package/deps/librdkafka/tests/test.h +62 -13
- package/deps/librdkafka/tests/testcpp.cpp +6 -1
- package/deps/librdkafka/tests/testcpp.h +30 -2
- package/deps/librdkafka/tests/testshared.h +16 -2
- package/deps/librdkafka/tests/tools/stats/graph.py +0 -0
- package/deps/librdkafka/tests/tools/stats/to_csv.py +1 -1
- package/deps/librdkafka/tests/trivup/trivup-0.14.0.tar.gz +0 -0
- package/deps/librdkafka/tests/until-fail.sh +0 -0
- package/deps/librdkafka/tests/xxxx-metadata.cpp +1 -1
- package/deps/librdkafka/vcpkg.json +1 -1
- package/deps/librdkafka/win32/setup-msys2.ps1 +24 -8
- package/deps/librdkafka/win32/setup-vcpkg.ps1 +28 -6
- package/deps/librdkafka/win32/tests/tests.vcxproj +6 -0
- package/deps/librdkafka/win32/wingetopt.h +5 -4
- package/errors.d.ts +27 -1
- package/lib/error.js +29 -3
- package/make_docs.sh +0 -0
- package/package.json +11 -11
- package/run_docker.sh +0 -0
- package/src/callbacks.cc +6 -9
- package/src/callbacks.h +1 -5
- package/test/kafka-consumer-worker.js +10 -7
- package/test/kafka-consumer.spec.js +32 -21
- package/ci/update-version.js +0 -123
- package/deps/librdkafka/.semaphore/project.yml +0 -43
- package/deps/librdkafka/.semaphore/project_public.yml +0 -20
- package/deps/librdkafka/win32/install-openssl.ps1 +0 -33
|
@@ -37,19 +37,21 @@
|
|
|
37
37
|
#include "rdkafka_mock_int.h"
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
static const char *
|
|
40
|
+
static const char *rd_kafka_mock_cgrp_classic_state_names[] = {
|
|
41
41
|
"Empty", "Joining", "Syncing", "Rebalancing", "Up"};
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
static void rd_kafka_mock_cgrp_rebalance(rd_kafka_mock_cgrp_t *mcgrp,
|
|
45
|
-
const char *reason);
|
|
46
44
|
static void
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
rd_kafka_mock_cgrp_classic_rebalance(rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
46
|
+
const char *reason);
|
|
47
|
+
static void rd_kafka_mock_cgrp_classic_member_destroy(
|
|
48
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
49
|
+
rd_kafka_mock_cgrp_classic_member_t *member);
|
|
49
50
|
|
|
50
|
-
static void
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
static void
|
|
52
|
+
rd_kafka_mock_cgrp_classic_set_state(rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
53
|
+
unsigned int new_state,
|
|
54
|
+
const char *reason) {
|
|
53
55
|
if (mcgrp->state == new_state)
|
|
54
56
|
return;
|
|
55
57
|
|
|
@@ -57,8 +59,8 @@ static void rd_kafka_mock_cgrp_set_state(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
57
59
|
"Mock consumer group %s with %d member(s) "
|
|
58
60
|
"changing state %s -> %s: %s",
|
|
59
61
|
mcgrp->id, mcgrp->member_cnt,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
rd_kafka_mock_cgrp_classic_state_names[mcgrp->state],
|
|
63
|
+
rd_kafka_mock_cgrp_classic_state_names[new_state], reason);
|
|
62
64
|
|
|
63
65
|
mcgrp->state = new_state;
|
|
64
66
|
}
|
|
@@ -67,8 +69,9 @@ static void rd_kafka_mock_cgrp_set_state(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
67
69
|
/**
|
|
68
70
|
* @brief Mark member as active (restart session timer)
|
|
69
71
|
*/
|
|
70
|
-
void
|
|
71
|
-
|
|
72
|
+
void rd_kafka_mock_cgrp_classic_member_active(
|
|
73
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
74
|
+
rd_kafka_mock_cgrp_classic_member_t *member) {
|
|
72
75
|
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
73
76
|
"Marking mock consumer group member %s as active",
|
|
74
77
|
member->id);
|
|
@@ -81,11 +84,11 @@ void rd_kafka_mock_cgrp_member_active(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
81
84
|
*
|
|
82
85
|
* @param member may be NULL.
|
|
83
86
|
*/
|
|
84
|
-
rd_kafka_resp_err_t
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
rd_kafka_resp_err_t rd_kafka_mock_cgrp_classic_check_state(
|
|
88
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
89
|
+
rd_kafka_mock_cgrp_classic_member_t *member,
|
|
90
|
+
const rd_kafka_buf_t *request,
|
|
91
|
+
int32_t generation_id) {
|
|
89
92
|
int16_t ApiKey = request->rkbuf_reqhdr.ApiKey;
|
|
90
93
|
rd_bool_t has_generation_id = ApiKey == RD_KAFKAP_SyncGroup ||
|
|
91
94
|
ApiKey == RD_KAFKAP_Heartbeat ||
|
|
@@ -142,9 +145,9 @@ rd_kafka_mock_cgrp_check_state(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
142
145
|
/**
|
|
143
146
|
* @brief Set a member's assignment (from leader's SyncGroupRequest)
|
|
144
147
|
*/
|
|
145
|
-
void
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
void rd_kafka_mock_cgrp_classic_member_assignment_set(
|
|
149
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
150
|
+
rd_kafka_mock_cgrp_classic_member_t *member,
|
|
148
151
|
const rd_kafkap_bytes_t *Metadata) {
|
|
149
152
|
if (member->assignment) {
|
|
150
153
|
rd_assert(mcgrp->assignment_cnt > 0);
|
|
@@ -163,9 +166,10 @@ void rd_kafka_mock_cgrp_member_assignment_set(
|
|
|
163
166
|
/**
|
|
164
167
|
* @brief Sync done (successfully) or failed, send responses back to members.
|
|
165
168
|
*/
|
|
166
|
-
static void
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
static void
|
|
170
|
+
rd_kafka_mock_cgrp_classic_sync_done(rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
171
|
+
rd_kafka_resp_err_t err) {
|
|
172
|
+
rd_kafka_mock_cgrp_classic_member_t *member;
|
|
169
173
|
|
|
170
174
|
TAILQ_FOREACH(member, &mcgrp->members, link) {
|
|
171
175
|
rd_kafka_buf_t *resp;
|
|
@@ -181,7 +185,8 @@ static void rd_kafka_mock_cgrp_sync_done(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
181
185
|
resp, !err ? member->assignment : NULL);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
|
|
188
|
+
rd_kafka_mock_cgrp_classic_member_assignment_set(mcgrp, member,
|
|
189
|
+
NULL);
|
|
185
190
|
|
|
186
191
|
if (member->conn) {
|
|
187
192
|
rd_kafka_mock_connection_set_blocking(member->conn,
|
|
@@ -201,20 +206,21 @@ static void rd_kafka_mock_cgrp_sync_done(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
201
206
|
* @brief Check if all members have sent SyncGroupRequests, if so, propagate
|
|
202
207
|
* assignment to members.
|
|
203
208
|
*/
|
|
204
|
-
static void
|
|
209
|
+
static void
|
|
210
|
+
rd_kafka_mock_cgrp_classic_sync_check(rd_kafka_mock_cgrp_classic_t *mcgrp) {
|
|
205
211
|
|
|
206
212
|
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
207
213
|
"Mock consumer group %s: awaiting %d/%d syncing members "
|
|
208
214
|
"in state %s",
|
|
209
215
|
mcgrp->id, mcgrp->assignment_cnt, mcgrp->member_cnt,
|
|
210
|
-
|
|
216
|
+
rd_kafka_mock_cgrp_classic_state_names[mcgrp->state]);
|
|
211
217
|
|
|
212
218
|
if (mcgrp->assignment_cnt < mcgrp->member_cnt)
|
|
213
219
|
return;
|
|
214
220
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
rd_kafka_mock_cgrp_classic_sync_done(mcgrp, RD_KAFKA_RESP_ERR_NO_ERROR);
|
|
222
|
+
rd_kafka_mock_cgrp_classic_set_state(mcgrp, RD_KAFKA_MOCK_CGRP_STATE_UP,
|
|
223
|
+
"all members synced");
|
|
218
224
|
}
|
|
219
225
|
|
|
220
226
|
|
|
@@ -223,16 +229,16 @@ static void rd_kafka_mock_cgrp_sync_check(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
223
229
|
* which will be sent when the all group member SyncGroupRequest are
|
|
224
230
|
* received.
|
|
225
231
|
*/
|
|
226
|
-
rd_kafka_resp_err_t
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
232
|
+
rd_kafka_resp_err_t rd_kafka_mock_cgrp_classic_member_sync_set(
|
|
233
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
234
|
+
rd_kafka_mock_cgrp_classic_member_t *member,
|
|
235
|
+
rd_kafka_mock_connection_t *mconn,
|
|
236
|
+
rd_kafka_buf_t *resp) {
|
|
231
237
|
|
|
232
238
|
if (mcgrp->state != RD_KAFKA_MOCK_CGRP_STATE_SYNCING)
|
|
233
239
|
return RD_KAFKA_RESP_ERR_REBALANCE_IN_PROGRESS; /* FIXME */
|
|
234
240
|
|
|
235
|
-
|
|
241
|
+
rd_kafka_mock_cgrp_classic_member_active(mcgrp, member);
|
|
236
242
|
|
|
237
243
|
rd_assert(!member->resp);
|
|
238
244
|
|
|
@@ -241,7 +247,7 @@ rd_kafka_mock_cgrp_member_sync_set(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
241
247
|
rd_kafka_mock_connection_set_blocking(member->conn, rd_true);
|
|
242
248
|
|
|
243
249
|
/* Check if all members now have an assignment, if so, send responses */
|
|
244
|
-
|
|
250
|
+
rd_kafka_mock_cgrp_classic_sync_check(mcgrp);
|
|
245
251
|
|
|
246
252
|
return RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
247
253
|
}
|
|
@@ -250,16 +256,16 @@ rd_kafka_mock_cgrp_member_sync_set(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
250
256
|
/**
|
|
251
257
|
* @brief Member is explicitly leaving the group (through LeaveGroupRequest)
|
|
252
258
|
*/
|
|
253
|
-
rd_kafka_resp_err_t
|
|
254
|
-
|
|
255
|
-
|
|
259
|
+
rd_kafka_resp_err_t rd_kafka_mock_cgrp_classic_member_leave(
|
|
260
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
261
|
+
rd_kafka_mock_cgrp_classic_member_t *member) {
|
|
256
262
|
|
|
257
263
|
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
258
264
|
"Member %s is leaving group %s", member->id, mcgrp->id);
|
|
259
265
|
|
|
260
|
-
|
|
266
|
+
rd_kafka_mock_cgrp_classic_member_destroy(mcgrp, member);
|
|
261
267
|
|
|
262
|
-
|
|
268
|
+
rd_kafka_mock_cgrp_classic_rebalance(mcgrp, "explicit member leave");
|
|
263
269
|
|
|
264
270
|
return RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
265
271
|
}
|
|
@@ -267,8 +273,9 @@ rd_kafka_mock_cgrp_member_leave(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
267
273
|
/**
|
|
268
274
|
* @brief Destroys/frees an array of protocols, including the array itself.
|
|
269
275
|
*/
|
|
270
|
-
void
|
|
271
|
-
|
|
276
|
+
void rd_kafka_mock_cgrp_classic_protos_destroy(
|
|
277
|
+
rd_kafka_mock_cgrp_classic_proto_t *protos,
|
|
278
|
+
int proto_cnt) {
|
|
272
279
|
int i;
|
|
273
280
|
|
|
274
281
|
for (i = 0; i < proto_cnt; i++) {
|
|
@@ -280,15 +287,16 @@ void rd_kafka_mock_cgrp_protos_destroy(rd_kafka_mock_cgrp_proto_t *protos,
|
|
|
280
287
|
rd_free(protos);
|
|
281
288
|
}
|
|
282
289
|
|
|
283
|
-
static void
|
|
284
|
-
|
|
285
|
-
|
|
290
|
+
static void rd_kafka_mock_cgrp_classic_rebalance_timer_restart(
|
|
291
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
292
|
+
int timeout_ms);
|
|
286
293
|
|
|
287
294
|
/**
|
|
288
295
|
* @brief Elect consumer group leader and send JoinGroup responses
|
|
289
296
|
*/
|
|
290
|
-
static void
|
|
291
|
-
|
|
297
|
+
static void
|
|
298
|
+
rd_kafka_mock_cgrp_classic_elect_leader(rd_kafka_mock_cgrp_classic_t *mcgrp) {
|
|
299
|
+
rd_kafka_mock_cgrp_classic_member_t *member;
|
|
292
300
|
|
|
293
301
|
rd_assert(mcgrp->state == RD_KAFKA_MOCK_CGRP_STATE_JOINING);
|
|
294
302
|
rd_assert(!TAILQ_EMPTY(&mcgrp->members));
|
|
@@ -331,7 +339,7 @@ static void rd_kafka_mock_cgrp_elect_leader(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
331
339
|
rd_bool_t is_leader = member == mcgrp->leader;
|
|
332
340
|
int member_cnt = is_leader ? mcgrp->member_cnt : 0;
|
|
333
341
|
rd_kafka_buf_t *resp;
|
|
334
|
-
|
|
342
|
+
rd_kafka_mock_cgrp_classic_member_t *member2;
|
|
335
343
|
rd_kafka_mock_connection_t *mconn;
|
|
336
344
|
|
|
337
345
|
/* Member connection has been closed, it will eventually
|
|
@@ -373,7 +381,7 @@ static void rd_kafka_mock_cgrp_elect_leader(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
373
381
|
/* Mark each member as active to avoid them timing out
|
|
374
382
|
* at the same time as a JoinGroup handler that blocks
|
|
375
383
|
* session.timeout.ms to elect a leader. */
|
|
376
|
-
|
|
384
|
+
rd_kafka_mock_cgrp_classic_member_active(mcgrp, member);
|
|
377
385
|
|
|
378
386
|
rd_kafka_mock_connection_set_blocking(mconn, rd_false);
|
|
379
387
|
rd_kafka_mock_connection_send_response(mconn, resp);
|
|
@@ -381,20 +389,22 @@ static void rd_kafka_mock_cgrp_elect_leader(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
381
389
|
|
|
382
390
|
mcgrp->last_member_cnt = mcgrp->member_cnt;
|
|
383
391
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
392
|
+
rd_kafka_mock_cgrp_classic_set_state(mcgrp,
|
|
393
|
+
RD_KAFKA_MOCK_CGRP_STATE_SYNCING,
|
|
394
|
+
"leader elected, waiting for all "
|
|
395
|
+
"members to sync");
|
|
387
396
|
|
|
388
|
-
|
|
389
|
-
|
|
397
|
+
rd_kafka_mock_cgrp_classic_rebalance_timer_restart(
|
|
398
|
+
mcgrp, mcgrp->session_timeout_ms);
|
|
390
399
|
}
|
|
391
400
|
|
|
392
401
|
|
|
393
402
|
/**
|
|
394
403
|
* @brief Trigger group rebalance.
|
|
395
404
|
*/
|
|
396
|
-
static void
|
|
397
|
-
|
|
405
|
+
static void
|
|
406
|
+
rd_kafka_mock_cgrp_classic_rebalance(rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
407
|
+
const char *reason) {
|
|
398
408
|
int timeout_ms;
|
|
399
409
|
|
|
400
410
|
if (mcgrp->state == RD_KAFKA_MOCK_CGRP_STATE_JOINING)
|
|
@@ -418,21 +428,23 @@ static void rd_kafka_mock_cgrp_rebalance(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
418
428
|
|
|
419
429
|
if (mcgrp->state == RD_KAFKA_MOCK_CGRP_STATE_SYNCING)
|
|
420
430
|
/* Abort current Syncing state */
|
|
421
|
-
|
|
431
|
+
rd_kafka_mock_cgrp_classic_sync_done(
|
|
422
432
|
mcgrp, RD_KAFKA_RESP_ERR_REBALANCE_IN_PROGRESS);
|
|
423
433
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
434
|
+
rd_kafka_mock_cgrp_classic_set_state(
|
|
435
|
+
mcgrp, RD_KAFKA_MOCK_CGRP_STATE_JOINING, reason);
|
|
436
|
+
rd_kafka_mock_cgrp_classic_rebalance_timer_restart(mcgrp, timeout_ms);
|
|
427
437
|
}
|
|
428
438
|
|
|
429
439
|
/**
|
|
430
440
|
* @brief Consumer group state machine triggered by timer events.
|
|
431
441
|
*/
|
|
432
|
-
static void
|
|
442
|
+
static void
|
|
443
|
+
rd_kafka_mock_cgrp_classic_fsm_timeout(rd_kafka_mock_cgrp_classic_t *mcgrp) {
|
|
433
444
|
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
434
445
|
"Mock consumer group %s FSM timeout in state %s",
|
|
435
|
-
mcgrp->id,
|
|
446
|
+
mcgrp->id,
|
|
447
|
+
rd_kafka_mock_cgrp_classic_state_names[mcgrp->state]);
|
|
436
448
|
|
|
437
449
|
switch (mcgrp->state) {
|
|
438
450
|
case RD_KAFKA_MOCK_CGRP_STATE_EMPTY:
|
|
@@ -441,9 +453,9 @@ static void rd_kafka_mock_cgrp_fsm_timeout(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
441
453
|
case RD_KAFKA_MOCK_CGRP_STATE_JOINING:
|
|
442
454
|
/* Timed out waiting for more members, elect a leader */
|
|
443
455
|
if (mcgrp->member_cnt > 0)
|
|
444
|
-
|
|
456
|
+
rd_kafka_mock_cgrp_classic_elect_leader(mcgrp);
|
|
445
457
|
else
|
|
446
|
-
|
|
458
|
+
rd_kafka_mock_cgrp_classic_set_state(
|
|
447
459
|
mcgrp, RD_KAFKA_MOCK_CGRP_STATE_EMPTY,
|
|
448
460
|
"no members joined");
|
|
449
461
|
break;
|
|
@@ -452,20 +464,20 @@ static void rd_kafka_mock_cgrp_fsm_timeout(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
452
464
|
/* Timed out waiting for all members to sync */
|
|
453
465
|
|
|
454
466
|
/* Send error response to all waiting members */
|
|
455
|
-
|
|
467
|
+
rd_kafka_mock_cgrp_classic_sync_done(
|
|
456
468
|
mcgrp, RD_KAFKA_RESP_ERR_REBALANCE_IN_PROGRESS /* FIXME */);
|
|
457
469
|
|
|
458
|
-
|
|
470
|
+
rd_kafka_mock_cgrp_classic_set_state(
|
|
459
471
|
mcgrp, RD_KAFKA_MOCK_CGRP_STATE_REBALANCING,
|
|
460
472
|
"timed out waiting for all members to synchronize");
|
|
461
473
|
break;
|
|
462
474
|
|
|
463
475
|
case RD_KAFKA_MOCK_CGRP_STATE_REBALANCING:
|
|
464
476
|
/* Timed out waiting for all members to Leave or re-Join */
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
477
|
+
rd_kafka_mock_cgrp_classic_set_state(
|
|
478
|
+
mcgrp, RD_KAFKA_MOCK_CGRP_STATE_JOINING,
|
|
479
|
+
"timed out waiting for all "
|
|
480
|
+
"members to re-Join or Leave");
|
|
469
481
|
break;
|
|
470
482
|
|
|
471
483
|
case RD_KAFKA_MOCK_CGRP_STATE_UP:
|
|
@@ -477,27 +489,27 @@ static void rd_kafka_mock_cgrp_fsm_timeout(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
477
489
|
|
|
478
490
|
static void rd_kafka_mcgrp_rebalance_timer_cb(rd_kafka_timers_t *rkts,
|
|
479
491
|
void *arg) {
|
|
480
|
-
|
|
492
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp = arg;
|
|
481
493
|
|
|
482
|
-
|
|
494
|
+
rd_kafka_mock_cgrp_classic_fsm_timeout(mcgrp);
|
|
483
495
|
}
|
|
484
496
|
|
|
485
497
|
|
|
486
498
|
/**
|
|
487
499
|
* @brief Restart the rebalance timer, postponing leader election.
|
|
488
500
|
*/
|
|
489
|
-
static void
|
|
490
|
-
|
|
491
|
-
|
|
501
|
+
static void rd_kafka_mock_cgrp_classic_rebalance_timer_restart(
|
|
502
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
503
|
+
int timeout_ms) {
|
|
492
504
|
rd_kafka_timer_start_oneshot(
|
|
493
505
|
&mcgrp->cluster->timers, &mcgrp->rebalance_tmr, rd_true,
|
|
494
506
|
timeout_ms * 1000, rd_kafka_mcgrp_rebalance_timer_cb, mcgrp);
|
|
495
507
|
}
|
|
496
508
|
|
|
497
509
|
|
|
498
|
-
static void
|
|
499
|
-
|
|
500
|
-
|
|
510
|
+
static void rd_kafka_mock_cgrp_classic_member_destroy(
|
|
511
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
512
|
+
rd_kafka_mock_cgrp_classic_member_t *member) {
|
|
501
513
|
rd_assert(mcgrp->member_cnt > 0);
|
|
502
514
|
TAILQ_REMOVE(&mcgrp->members, member, link);
|
|
503
515
|
mcgrp->member_cnt--;
|
|
@@ -510,9 +522,10 @@ rd_kafka_mock_cgrp_member_destroy(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
510
522
|
if (member->group_instance_id)
|
|
511
523
|
rd_free(member->group_instance_id);
|
|
512
524
|
|
|
513
|
-
|
|
525
|
+
rd_kafka_mock_cgrp_classic_member_assignment_set(mcgrp, member, NULL);
|
|
514
526
|
|
|
515
|
-
|
|
527
|
+
rd_kafka_mock_cgrp_classic_protos_destroy(member->protos,
|
|
528
|
+
member->proto_cnt);
|
|
516
529
|
|
|
517
530
|
rd_free(member);
|
|
518
531
|
}
|
|
@@ -521,13 +534,13 @@ rd_kafka_mock_cgrp_member_destroy(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
521
534
|
/**
|
|
522
535
|
* @brief Find member in group.
|
|
523
536
|
*/
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const
|
|
537
|
+
rd_kafka_mock_cgrp_classic_member_t *rd_kafka_mock_cgrp_classic_member_find(
|
|
538
|
+
const rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
539
|
+
const rd_kafkap_str_t *MemberId) {
|
|
540
|
+
const rd_kafka_mock_cgrp_classic_member_t *member;
|
|
528
541
|
TAILQ_FOREACH(member, &mcgrp->members, link) {
|
|
529
542
|
if (!rd_kafkap_str_cmp_str(MemberId, member->id))
|
|
530
|
-
return (
|
|
543
|
+
return (rd_kafka_mock_cgrp_classic_member_t *)member;
|
|
531
544
|
}
|
|
532
545
|
|
|
533
546
|
return NULL;
|
|
@@ -537,25 +550,25 @@ rd_kafka_mock_cgrp_member_find(const rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
537
550
|
/**
|
|
538
551
|
* @brief Update or add member to consumer group
|
|
539
552
|
*/
|
|
540
|
-
rd_kafka_resp_err_t
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
553
|
+
rd_kafka_resp_err_t rd_kafka_mock_cgrp_classic_member_add(
|
|
554
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp,
|
|
555
|
+
rd_kafka_mock_connection_t *mconn,
|
|
556
|
+
rd_kafka_buf_t *resp,
|
|
557
|
+
const rd_kafkap_str_t *MemberId,
|
|
558
|
+
const rd_kafkap_str_t *ProtocolType,
|
|
559
|
+
const rd_kafkap_str_t *GroupInstanceId,
|
|
560
|
+
rd_kafka_mock_cgrp_classic_proto_t *protos,
|
|
561
|
+
int proto_cnt,
|
|
562
|
+
int session_timeout_ms) {
|
|
563
|
+
rd_kafka_mock_cgrp_classic_member_t *member;
|
|
551
564
|
rd_kafka_resp_err_t err;
|
|
552
565
|
|
|
553
|
-
err =
|
|
566
|
+
err = rd_kafka_mock_cgrp_classic_check_state(mcgrp, NULL, resp, -1);
|
|
554
567
|
if (err)
|
|
555
568
|
return err;
|
|
556
569
|
|
|
557
570
|
/* Find member */
|
|
558
|
-
member =
|
|
571
|
+
member = rd_kafka_mock_cgrp_classic_member_find(mcgrp, MemberId);
|
|
559
572
|
if (!member) {
|
|
560
573
|
/* Not found, add member */
|
|
561
574
|
member = rd_calloc(1, sizeof(*member));
|
|
@@ -577,20 +590,20 @@ rd_kafka_mock_cgrp_member_add(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
577
590
|
}
|
|
578
591
|
|
|
579
592
|
if (mcgrp->state != RD_KAFKA_MOCK_CGRP_STATE_JOINING)
|
|
580
|
-
|
|
593
|
+
rd_kafka_mock_cgrp_classic_rebalance(mcgrp, "member join");
|
|
581
594
|
|
|
582
595
|
mcgrp->session_timeout_ms = session_timeout_ms;
|
|
583
596
|
|
|
584
597
|
if (member->protos)
|
|
585
|
-
|
|
586
|
-
|
|
598
|
+
rd_kafka_mock_cgrp_classic_protos_destroy(member->protos,
|
|
599
|
+
member->proto_cnt);
|
|
587
600
|
member->protos = protos;
|
|
588
601
|
member->proto_cnt = proto_cnt;
|
|
589
602
|
|
|
590
603
|
rd_assert(!member->resp);
|
|
591
604
|
member->resp = resp;
|
|
592
605
|
member->conn = mconn;
|
|
593
|
-
|
|
606
|
+
rd_kafka_mock_cgrp_classic_member_active(mcgrp, member);
|
|
594
607
|
|
|
595
608
|
return RD_KAFKA_RESP_ERR_NO_ERROR;
|
|
596
609
|
}
|
|
@@ -598,10 +611,10 @@ rd_kafka_mock_cgrp_member_add(rd_kafka_mock_cgrp_t *mcgrp,
|
|
|
598
611
|
/**
|
|
599
612
|
* @brief Check if any members have exceeded the session timeout.
|
|
600
613
|
*/
|
|
601
|
-
static void
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
614
|
+
static void rd_kafka_mock_cgrp_classic_session_tmr_cb(rd_kafka_timers_t *rkts,
|
|
615
|
+
void *arg) {
|
|
616
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp = arg;
|
|
617
|
+
rd_kafka_mock_cgrp_classic_member_t *member, *tmp;
|
|
605
618
|
rd_ts_t now = rd_clock();
|
|
606
619
|
int timeout_cnt = 0;
|
|
607
620
|
|
|
@@ -615,19 +628,19 @@ static void rd_kafka_mock_cgrp_session_tmr_cb(rd_kafka_timers_t *rkts,
|
|
|
615
628
|
"Member %s session timed out for group %s",
|
|
616
629
|
member->id, mcgrp->id);
|
|
617
630
|
|
|
618
|
-
|
|
631
|
+
rd_kafka_mock_cgrp_classic_member_destroy(mcgrp, member);
|
|
619
632
|
timeout_cnt++;
|
|
620
633
|
}
|
|
621
634
|
|
|
622
635
|
if (timeout_cnt)
|
|
623
|
-
|
|
636
|
+
rd_kafka_mock_cgrp_classic_rebalance(mcgrp, "member timeout");
|
|
624
637
|
}
|
|
625
638
|
|
|
626
639
|
|
|
627
|
-
void
|
|
628
|
-
|
|
640
|
+
void rd_kafka_mock_cgrp_classic_destroy(rd_kafka_mock_cgrp_classic_t *mcgrp) {
|
|
641
|
+
rd_kafka_mock_cgrp_classic_member_t *member;
|
|
629
642
|
|
|
630
|
-
TAILQ_REMOVE(&mcgrp->cluster->
|
|
643
|
+
TAILQ_REMOVE(&mcgrp->cluster->cgrps_classic, mcgrp, link);
|
|
631
644
|
|
|
632
645
|
rd_kafka_timer_stop(&mcgrp->cluster->timers, &mcgrp->rebalance_tmr,
|
|
633
646
|
rd_true);
|
|
@@ -638,15 +651,16 @@ void rd_kafka_mock_cgrp_destroy(rd_kafka_mock_cgrp_t *mcgrp) {
|
|
|
638
651
|
if (mcgrp->protocol_name)
|
|
639
652
|
rd_free(mcgrp->protocol_name);
|
|
640
653
|
while ((member = TAILQ_FIRST(&mcgrp->members)))
|
|
641
|
-
|
|
654
|
+
rd_kafka_mock_cgrp_classic_member_destroy(mcgrp, member);
|
|
642
655
|
rd_free(mcgrp);
|
|
643
656
|
}
|
|
644
657
|
|
|
645
658
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
659
|
+
rd_kafka_mock_cgrp_classic_t *
|
|
660
|
+
rd_kafka_mock_cgrp_classic_find(rd_kafka_mock_cluster_t *mcluster,
|
|
661
|
+
const rd_kafkap_str_t *GroupId) {
|
|
662
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp;
|
|
663
|
+
TAILQ_FOREACH(mcgrp, &mcluster->cgrps_classic, link) {
|
|
650
664
|
if (!rd_kafkap_str_cmp_str(GroupId, mcgrp->id))
|
|
651
665
|
return mcgrp;
|
|
652
666
|
}
|
|
@@ -656,15 +670,15 @@ rd_kafka_mock_cgrp_t *rd_kafka_mock_cgrp_find(rd_kafka_mock_cluster_t *mcluster,
|
|
|
656
670
|
|
|
657
671
|
|
|
658
672
|
/**
|
|
659
|
-
* @brief Find or create a consumer group
|
|
673
|
+
* @brief Find or create a classic consumer group
|
|
660
674
|
*/
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
675
|
+
rd_kafka_mock_cgrp_classic_t *
|
|
676
|
+
rd_kafka_mock_cgrp_classic_get(rd_kafka_mock_cluster_t *mcluster,
|
|
677
|
+
const rd_kafkap_str_t *GroupId,
|
|
678
|
+
const rd_kafkap_str_t *ProtocolType) {
|
|
679
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp;
|
|
666
680
|
|
|
667
|
-
mcgrp =
|
|
681
|
+
mcgrp = rd_kafka_mock_cgrp_classic_find(mcluster, GroupId);
|
|
668
682
|
if (mcgrp)
|
|
669
683
|
return mcgrp;
|
|
670
684
|
|
|
@@ -679,24 +693,25 @@ rd_kafka_mock_cgrp_get(rd_kafka_mock_cluster_t *mcluster,
|
|
|
679
693
|
TAILQ_INIT(&mcgrp->members);
|
|
680
694
|
rd_kafka_timer_start(&mcluster->timers, &mcgrp->session_tmr,
|
|
681
695
|
1000 * 1000 /*1s*/,
|
|
682
|
-
|
|
696
|
+
rd_kafka_mock_cgrp_classic_session_tmr_cb, mcgrp);
|
|
683
697
|
|
|
684
|
-
TAILQ_INSERT_TAIL(&mcluster->
|
|
698
|
+
TAILQ_INSERT_TAIL(&mcluster->cgrps_classic, mcgrp, link);
|
|
685
699
|
|
|
686
700
|
return mcgrp;
|
|
687
701
|
}
|
|
688
702
|
|
|
689
703
|
|
|
690
704
|
/**
|
|
691
|
-
* @brief A client connection closed, check if any cgrp has any state
|
|
705
|
+
* @brief A client connection closed, check if any classic cgrp has any state
|
|
692
706
|
* for this connection that needs to be cleared.
|
|
693
707
|
*/
|
|
694
|
-
void
|
|
695
|
-
|
|
696
|
-
|
|
708
|
+
void rd_kafka_mock_cgrps_classic_connection_closed(
|
|
709
|
+
rd_kafka_mock_cluster_t *mcluster,
|
|
710
|
+
rd_kafka_mock_connection_t *mconn) {
|
|
711
|
+
rd_kafka_mock_cgrp_classic_t *mcgrp;
|
|
697
712
|
|
|
698
|
-
TAILQ_FOREACH(mcgrp, &mcluster->
|
|
699
|
-
|
|
713
|
+
TAILQ_FOREACH(mcgrp, &mcluster->cgrps_classic, link) {
|
|
714
|
+
rd_kafka_mock_cgrp_classic_member_t *member, *tmp;
|
|
700
715
|
TAILQ_FOREACH_SAFE(member, &mcgrp->members, link, tmp) {
|
|
701
716
|
if (member->conn == mconn) {
|
|
702
717
|
member->conn = NULL;
|
|
@@ -708,3 +723,1154 @@ void rd_kafka_mock_cgrps_connection_closed(rd_kafka_mock_cluster_t *mcluster,
|
|
|
708
723
|
}
|
|
709
724
|
}
|
|
710
725
|
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* @struct Target assignment for a consumer group.
|
|
729
|
+
* `member_ids` and `assignment` are in the same order
|
|
730
|
+
* and have the same count.
|
|
731
|
+
*/
|
|
732
|
+
typedef struct rd_kafka_mock_cgrp_consumer_target_assignment_s {
|
|
733
|
+
rd_list_t *member_ids; /**< Member id list (char *). */
|
|
734
|
+
rd_list_t *assignment; /**< Assingment list
|
|
735
|
+
(rd_kafka_topic_partition_list_t *). */
|
|
736
|
+
} rd_kafka_mock_cgrp_consumer_target_assignment_t;
|
|
737
|
+
|
|
738
|
+
static rd_kafka_mock_cgrp_consumer_target_assignment_t *
|
|
739
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_new0(rd_list_t *member_ids,
|
|
740
|
+
rd_list_t *assignment) {
|
|
741
|
+
rd_assert(member_ids->rl_cnt == assignment->rl_cnt);
|
|
742
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *target_assignment =
|
|
743
|
+
rd_calloc(1, sizeof(*target_assignment));
|
|
744
|
+
target_assignment->member_ids =
|
|
745
|
+
rd_list_copy(member_ids, rd_list_string_copy, NULL);
|
|
746
|
+
target_assignment->assignment = rd_list_copy(
|
|
747
|
+
assignment, rd_kafka_topic_partition_list_copy_opaque, NULL);
|
|
748
|
+
return target_assignment;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *
|
|
752
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_new(
|
|
753
|
+
char **member_ids,
|
|
754
|
+
int member_cnt,
|
|
755
|
+
rd_kafka_topic_partition_list_t **assignment) {
|
|
756
|
+
int i;
|
|
757
|
+
rd_list_t *member_id_list, *assignment_list;
|
|
758
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *ret;
|
|
759
|
+
|
|
760
|
+
member_id_list = rd_list_new(member_cnt, rd_free);
|
|
761
|
+
assignment_list =
|
|
762
|
+
rd_list_new(member_cnt, rd_kafka_topic_partition_list_destroy_free);
|
|
763
|
+
for (i = 0; i < member_cnt; i++) {
|
|
764
|
+
rd_list_add(member_id_list, rd_strdup(member_ids[i]));
|
|
765
|
+
rd_list_add(assignment_list,
|
|
766
|
+
rd_kafka_topic_partition_list_copy(assignment[i]));
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
ret = rd_kafka_mock_cgrp_consumer_target_assignment_new0(
|
|
770
|
+
member_id_list, assignment_list);
|
|
771
|
+
rd_list_destroy(member_id_list);
|
|
772
|
+
rd_list_destroy(assignment_list);
|
|
773
|
+
return ret;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
void rd_kafka_mock_cgrp_consumer_target_assignment_destroy(
|
|
777
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *target_assignment) {
|
|
778
|
+
rd_list_destroy(target_assignment->member_ids);
|
|
779
|
+
rd_list_destroy(target_assignment->assignment);
|
|
780
|
+
rd_free(target_assignment);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* @brief Sets next target assignment and member epoch for \p member
|
|
785
|
+
* to a copy of partition list \p rktparlist,
|
|
786
|
+
* filling its topic ids if not provided, using \p cgrp cluster topics.
|
|
787
|
+
*
|
|
788
|
+
* @param mcgrp The consumer group containing the member.
|
|
789
|
+
* @param member A consumer group member.
|
|
790
|
+
* @param target_member_epoch New member epoch.
|
|
791
|
+
* @param rktparlist Next target assignment.
|
|
792
|
+
*
|
|
793
|
+
* @locks mcluster->lock MUST be held.
|
|
794
|
+
*/
|
|
795
|
+
static void rd_kafka_mock_cgrp_consumer_member_target_assignment_set(
|
|
796
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
797
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
798
|
+
int target_member_epoch,
|
|
799
|
+
const rd_kafka_topic_partition_list_t *rktparlist) {
|
|
800
|
+
rd_kafka_topic_partition_t *rktpar;
|
|
801
|
+
if (member->target_assignment) {
|
|
802
|
+
rd_kafka_topic_partition_list_destroy(
|
|
803
|
+
member->target_assignment);
|
|
804
|
+
}
|
|
805
|
+
member->target_member_epoch = target_member_epoch;
|
|
806
|
+
member->target_assignment =
|
|
807
|
+
rd_kafka_topic_partition_list_copy(rktparlist);
|
|
808
|
+
|
|
809
|
+
/* If not present, fill topic ids using names */
|
|
810
|
+
RD_KAFKA_TPLIST_FOREACH(rktpar, member->target_assignment) {
|
|
811
|
+
rd_kafka_Uuid_t topic_id =
|
|
812
|
+
rd_kafka_topic_partition_get_topic_id(rktpar);
|
|
813
|
+
if (!rd_kafka_Uuid_cmp(topic_id, RD_KAFKA_UUID_ZERO)) {
|
|
814
|
+
rd_kafka_mock_topic_t *mtopic =
|
|
815
|
+
rd_kafka_mock_topic_find(mcgrp->cluster,
|
|
816
|
+
rktpar->topic);
|
|
817
|
+
if (mtopic)
|
|
818
|
+
rd_kafka_topic_partition_set_topic_id(
|
|
819
|
+
rktpar, mtopic->id);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* @brief Sets next target assignment for group \p mcgrp
|
|
826
|
+
* to a copy of \p target_assignment partition lists.
|
|
827
|
+
*
|
|
828
|
+
* @param mcgrp The consumer group.
|
|
829
|
+
* @param target_assignment Target assignment for all members.
|
|
830
|
+
*
|
|
831
|
+
* @locks mcluster->lock MUST be held.
|
|
832
|
+
*/
|
|
833
|
+
static void rd_kafka_mock_cgrp_consumer_target_assignment_set(
|
|
834
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
835
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *target_assignment) {
|
|
836
|
+
int i = 0;
|
|
837
|
+
int32_t new_target_member_epoch;
|
|
838
|
+
const char *member_id;
|
|
839
|
+
rd_kafka_mock_cgrp_consumer_member_t *member;
|
|
840
|
+
|
|
841
|
+
mcgrp->group_epoch++;
|
|
842
|
+
new_target_member_epoch = mcgrp->group_epoch;
|
|
843
|
+
RD_LIST_FOREACH(member_id, target_assignment->member_ids, i) {
|
|
844
|
+
rd_kafkap_str_t *member_id_str =
|
|
845
|
+
rd_kafkap_str_new(member_id, strlen(member_id));
|
|
846
|
+
rd_kafka_topic_partition_list_t *member_assignment =
|
|
847
|
+
rd_list_elem(target_assignment->assignment, i);
|
|
848
|
+
member = rd_kafka_mock_cgrp_consumer_member_find(mcgrp,
|
|
849
|
+
member_id_str);
|
|
850
|
+
rd_kafkap_str_destroy(member_id_str);
|
|
851
|
+
|
|
852
|
+
if (!member)
|
|
853
|
+
continue;
|
|
854
|
+
|
|
855
|
+
rd_kafka_mock_cgrp_consumer_member_target_assignment_set(
|
|
856
|
+
mcgrp, member, new_target_member_epoch, member_assignment);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
typedef RD_MAP_TYPE(const char *, rd_list_t *) map_str_list;
|
|
861
|
+
typedef RD_MAP_TYPE(const char *, int *) map_str_int;
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* @brief Calculate a simple range target assignment for the consumer group \p
|
|
865
|
+
* mcgrp. This isn't replicating any given broker assignor but is used
|
|
866
|
+
* when the test doesn't need a specific type of assignment.
|
|
867
|
+
*
|
|
868
|
+
* If the test needs it, instead of replicating same conditions with all the
|
|
869
|
+
* members, one can mock the assignment directly with
|
|
870
|
+
* `rd_kafka_mock_cgrp_consumer_target_assignment`.
|
|
871
|
+
*/
|
|
872
|
+
static rd_kafka_mock_cgrp_consumer_target_assignment_t *
|
|
873
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_calculate_range(
|
|
874
|
+
const rd_kafka_mock_cgrp_consumer_t *mcgrp) {
|
|
875
|
+
int i, *i_pointer;
|
|
876
|
+
const char *topic;
|
|
877
|
+
rd_list_t *members;
|
|
878
|
+
rd_kafka_mock_cgrp_consumer_member_t *member;
|
|
879
|
+
rd_kafka_mock_cluster_t *mcluster = mcgrp->cluster;
|
|
880
|
+
/* List of member ids (char *) */
|
|
881
|
+
rd_list_t *member_ids = rd_list_new(mcgrp->member_cnt, rd_free);
|
|
882
|
+
/* List of member assignment (rd_kafka_topic_partition_list_t *) */
|
|
883
|
+
rd_list_t *assignment = rd_list_new(
|
|
884
|
+
mcgrp->member_cnt, rd_kafka_topic_partition_list_destroy_free);
|
|
885
|
+
/* Map from topic name to list of members */
|
|
886
|
+
map_str_list topic_members =
|
|
887
|
+
RD_MAP_INITIALIZER(mcgrp->member_cnt, rd_map_str_cmp,
|
|
888
|
+
rd_map_str_hash, NULL, rd_list_destroy_free);
|
|
889
|
+
/* Map from member id to index in the members and assignment lists. */
|
|
890
|
+
map_str_int member_idx = RD_MAP_INITIALIZER(
|
|
891
|
+
mcgrp->member_cnt, rd_map_str_cmp, rd_map_str_hash, NULL, rd_free);
|
|
892
|
+
|
|
893
|
+
i = 0;
|
|
894
|
+
|
|
895
|
+
/* First create a map with topics associated to the list of members
|
|
896
|
+
* and save the member idx in the `member_idx` map. */
|
|
897
|
+
TAILQ_FOREACH(member, &mcgrp->members, link) {
|
|
898
|
+
int j;
|
|
899
|
+
rd_list_add(member_ids, rd_strdup(member->id));
|
|
900
|
+
rd_list_add(assignment, rd_kafka_topic_partition_list_new(0));
|
|
901
|
+
|
|
902
|
+
RD_LIST_FOREACH(topic, member->subscribed_topics, j) {
|
|
903
|
+
if (!RD_MAP_GET(&topic_members, topic)) {
|
|
904
|
+
members = rd_list_new(0, NULL);
|
|
905
|
+
RD_MAP_SET(&topic_members, topic, members);
|
|
906
|
+
} else
|
|
907
|
+
members = RD_MAP_GET(&topic_members, topic);
|
|
908
|
+
rd_list_add(members, member);
|
|
909
|
+
}
|
|
910
|
+
i_pointer = rd_calloc(1, sizeof(*i_pointer));
|
|
911
|
+
*i_pointer = i;
|
|
912
|
+
RD_MAP_SET(&member_idx, member->id, i_pointer);
|
|
913
|
+
i++;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/* For each topic to a range assignment and add the
|
|
917
|
+
* corresponding partitions to the assignment for that member.
|
|
918
|
+
* Finds the list index using the `member_idx` map. */
|
|
919
|
+
RD_MAP_FOREACH(topic, members, &topic_members) {
|
|
920
|
+
rd_kafka_Uuid_t topic_id;
|
|
921
|
+
rd_kafka_topic_partition_list_t *member_assignment;
|
|
922
|
+
int members_cnt = rd_list_cnt(members);
|
|
923
|
+
int common, one_more, assigned = 0;
|
|
924
|
+
rd_kafkap_str_t Topic = {.str = topic, .len = strlen(topic)};
|
|
925
|
+
rd_kafka_mock_topic_t *mock_topic =
|
|
926
|
+
rd_kafka_mock_topic_find_by_kstr(mcluster, &Topic);
|
|
927
|
+
if (!mock_topic)
|
|
928
|
+
continue;
|
|
929
|
+
|
|
930
|
+
topic_id = mock_topic->id;
|
|
931
|
+
|
|
932
|
+
/* Assign one partition more
|
|
933
|
+
* to the first mock_topic->partition_cnt % members_cnt
|
|
934
|
+
* members. */
|
|
935
|
+
common = mock_topic->partition_cnt / members_cnt;
|
|
936
|
+
one_more = mock_topic->partition_cnt % members_cnt;
|
|
937
|
+
|
|
938
|
+
RD_LIST_FOREACH(member, members, i) {
|
|
939
|
+
int j, num_partitions = common;
|
|
940
|
+
int idx = *RD_MAP_GET(&member_idx, member->id);
|
|
941
|
+
member_assignment = rd_list_elem(assignment, idx);
|
|
942
|
+
if (idx < one_more)
|
|
943
|
+
num_partitions++;
|
|
944
|
+
for (j = 0; j < num_partitions; j++) {
|
|
945
|
+
rd_kafka_topic_partition_t *rktpar =
|
|
946
|
+
rd_kafka_topic_partition_list_add(
|
|
947
|
+
member_assignment, topic, assigned + j);
|
|
948
|
+
rd_kafka_topic_partition_set_topic_id(rktpar,
|
|
949
|
+
topic_id);
|
|
950
|
+
}
|
|
951
|
+
assigned += num_partitions;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *ret =
|
|
956
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_new0(member_ids,
|
|
957
|
+
assignment);
|
|
958
|
+
|
|
959
|
+
RD_MAP_DESTROY(&topic_members);
|
|
960
|
+
RD_MAP_DESTROY(&member_idx);
|
|
961
|
+
|
|
962
|
+
rd_list_destroy(member_ids);
|
|
963
|
+
rd_list_destroy(assignment);
|
|
964
|
+
|
|
965
|
+
return ret;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* @brief Recalculate and set a target assignment for \p mcgrp
|
|
970
|
+
* only if `mcgrp->manual_assignment` isn't set.
|
|
971
|
+
*
|
|
972
|
+
* @locks mcluster->lock MUST be held.
|
|
973
|
+
*/
|
|
974
|
+
static void rd_kafka_mock_cgrp_consumer_target_assignment_recalculate(
|
|
975
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp) {
|
|
976
|
+
if (mcgrp->manual_assignment)
|
|
977
|
+
return;
|
|
978
|
+
|
|
979
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *target_assignment =
|
|
980
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_calculate_range(
|
|
981
|
+
mcgrp);
|
|
982
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_set(mcgrp,
|
|
983
|
+
target_assignment);
|
|
984
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_destroy(
|
|
985
|
+
target_assignment);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* @brief Set manual target assignment \p target_assignment
|
|
990
|
+
* to the consumer group \p mcgrp .
|
|
991
|
+
*
|
|
992
|
+
* @param mcgrp Consumer group
|
|
993
|
+
* @param target_assignment Target assignment to set.
|
|
994
|
+
* Pass NULL to return to automatic assignment.
|
|
995
|
+
*
|
|
996
|
+
* @locks mcluster->lock MUST be held.
|
|
997
|
+
*/
|
|
998
|
+
static void rd_kafka_mock_cgrp_consumer_target_assignment_set_manual(
|
|
999
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1000
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *target_assignment) {
|
|
1001
|
+
if (!target_assignment) {
|
|
1002
|
+
mcgrp->manual_assignment = rd_false;
|
|
1003
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_recalculate(
|
|
1004
|
+
mcgrp);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
mcgrp->manual_assignment = rd_true;
|
|
1009
|
+
|
|
1010
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_set(mcgrp,
|
|
1011
|
+
target_assignment);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* @brief Sets \p member current assignment to a copy of
|
|
1016
|
+
* \p current_assignment.
|
|
1017
|
+
*
|
|
1018
|
+
* @param member A consumer group member.
|
|
1019
|
+
* @param current_assignment Current assignment to set.
|
|
1020
|
+
*
|
|
1021
|
+
* @locks mcluster->lock MUST be held.
|
|
1022
|
+
*/
|
|
1023
|
+
static void rd_kafka_mock_cgrp_consumer_member_current_assignment_set(
|
|
1024
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1025
|
+
const rd_kafka_topic_partition_list_t *current_assignment) {
|
|
1026
|
+
if (member->current_assignment) {
|
|
1027
|
+
rd_kafka_topic_partition_list_destroy(
|
|
1028
|
+
member->current_assignment);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
member->current_assignment =
|
|
1032
|
+
current_assignment
|
|
1033
|
+
? rd_kafka_topic_partition_list_copy(current_assignment)
|
|
1034
|
+
: NULL;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* @brief Sets \p member returned assignment to a
|
|
1039
|
+
* copy of \p returned_assignment.
|
|
1040
|
+
*
|
|
1041
|
+
* @param member A consumer group member.
|
|
1042
|
+
* @param returned_assignment Returned assignment to set.
|
|
1043
|
+
*
|
|
1044
|
+
* @locks mcluster->lock MUST be held.
|
|
1045
|
+
*/
|
|
1046
|
+
static void rd_kafka_mock_cgrp_consumer_member_returned_assignment_set(
|
|
1047
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1048
|
+
const rd_kafka_topic_partition_list_t *returned_assignment) {
|
|
1049
|
+
if (member->returned_assignment) {
|
|
1050
|
+
rd_kafka_topic_partition_list_destroy(
|
|
1051
|
+
member->returned_assignment);
|
|
1052
|
+
}
|
|
1053
|
+
member->returned_assignment =
|
|
1054
|
+
returned_assignment
|
|
1055
|
+
? rd_kafka_topic_partition_list_copy(returned_assignment)
|
|
1056
|
+
: NULL;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* @brief Returns a copy of \p member target assignment containing only
|
|
1061
|
+
* partitions that can be assignment, whose topic id is non-zero.
|
|
1062
|
+
*
|
|
1063
|
+
* @param member The group member.
|
|
1064
|
+
*
|
|
1065
|
+
* @remark The returned pointer ownership is transferred to the caller.
|
|
1066
|
+
*
|
|
1067
|
+
* @locks mcluster->lock MUST be held.
|
|
1068
|
+
*/
|
|
1069
|
+
static rd_kafka_topic_partition_list_t *
|
|
1070
|
+
rd_kafka_mock_cgrp_consumer_member_target_assignment_assignable(
|
|
1071
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1072
|
+
rd_kafka_topic_partition_list_t *assignment = member->target_assignment;
|
|
1073
|
+
rd_kafka_topic_partition_t *rktpar;
|
|
1074
|
+
rd_kafka_topic_partition_list_t *ret =
|
|
1075
|
+
rd_kafka_topic_partition_list_new(assignment->cnt);
|
|
1076
|
+
|
|
1077
|
+
RD_KAFKA_TPLIST_FOREACH(rktpar, assignment) {
|
|
1078
|
+
rd_kafka_Uuid_t topic_id =
|
|
1079
|
+
rd_kafka_topic_partition_get_topic_id(rktpar);
|
|
1080
|
+
if (rd_kafka_Uuid_cmp(topic_id, RD_KAFKA_UUID_ZERO)) {
|
|
1081
|
+
rd_kafka_topic_partition_list_add_copy(ret, rktpar);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
return ret;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Returns true iff \p new_assignment doesn't have any intersection with any
|
|
1090
|
+
* other member current assignment.
|
|
1091
|
+
*
|
|
1092
|
+
* If there's an intersection, it means we cannot bump the epoch at the moment,
|
|
1093
|
+
* because some of these partitions are held by a different member. They have
|
|
1094
|
+
* to be revoked from that member before it's possible to increase the epoch
|
|
1095
|
+
* and assign additional partitions to this member.
|
|
1096
|
+
*/
|
|
1097
|
+
rd_bool_t rd_kafka_mock_cgrp_consumer_member_next_assignment_can_bump_epoch(
|
|
1098
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1099
|
+
rd_kafka_topic_partition_list_t *new_assignment) {
|
|
1100
|
+
rd_kafka_topic_partition_list_t *double_assignment,
|
|
1101
|
+
*assigned_partitions = rd_kafka_topic_partition_list_new(0);
|
|
1102
|
+
rd_kafka_mock_cgrp_consumer_member_t *other_member;
|
|
1103
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp = member->mcgrp;
|
|
1104
|
+
rd_bool_t ret;
|
|
1105
|
+
|
|
1106
|
+
TAILQ_FOREACH(other_member, &mcgrp->members, link) {
|
|
1107
|
+
int other_current_assignment_cnt = 0,
|
|
1108
|
+
other_returned_assignment_cnt = 0;
|
|
1109
|
+
if (member == other_member)
|
|
1110
|
+
continue;
|
|
1111
|
+
if (other_member->current_assignment)
|
|
1112
|
+
other_current_assignment_cnt =
|
|
1113
|
+
other_member->current_assignment->cnt;
|
|
1114
|
+
if (other_member->returned_assignment)
|
|
1115
|
+
other_returned_assignment_cnt =
|
|
1116
|
+
other_member->returned_assignment->cnt;
|
|
1117
|
+
|
|
1118
|
+
if (other_current_assignment_cnt > 0 &&
|
|
1119
|
+
other_current_assignment_cnt >
|
|
1120
|
+
other_returned_assignment_cnt) {
|
|
1121
|
+
/* This is the case where we're revoking
|
|
1122
|
+
* some partitions.
|
|
1123
|
+
* returned_assignment < current_assignment. */
|
|
1124
|
+
rd_kafka_topic_partition_list_add_list(
|
|
1125
|
+
assigned_partitions,
|
|
1126
|
+
other_member->current_assignment);
|
|
1127
|
+
} else if (other_returned_assignment_cnt > 0) {
|
|
1128
|
+
/* This is the case where we're assigning
|
|
1129
|
+
* some partitions.
|
|
1130
|
+
* returned_assignment >= current_assignment. */
|
|
1131
|
+
rd_kafka_topic_partition_list_add_list(
|
|
1132
|
+
assigned_partitions,
|
|
1133
|
+
other_member->returned_assignment);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
double_assignment = rd_kafka_topic_partition_list_intersection_by_id(
|
|
1137
|
+
new_assignment, assigned_partitions);
|
|
1138
|
+
ret = double_assignment->cnt == 0;
|
|
1139
|
+
|
|
1140
|
+
rd_kafka_topic_partition_list_destroy(assigned_partitions);
|
|
1141
|
+
rd_kafka_topic_partition_list_destroy(double_assignment);
|
|
1142
|
+
return ret;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* @brief Calculates if \p member,
|
|
1147
|
+
* needs a revocation, that is if its current assignment
|
|
1148
|
+
* isn't a subset of its target assignment.
|
|
1149
|
+
* In case it needs a revocation, it returns
|
|
1150
|
+
* the intersection between the two assignments,
|
|
1151
|
+
* that is the remaining partitions after revocation
|
|
1152
|
+
* of those not included in target assignment.
|
|
1153
|
+
*
|
|
1154
|
+
* @param member The group member.
|
|
1155
|
+
*
|
|
1156
|
+
* @return The remaining set of partitions, or NULL in case no revocation
|
|
1157
|
+
* is needed.
|
|
1158
|
+
*
|
|
1159
|
+
* @remark The returned pointer ownership is transferred to the caller.
|
|
1160
|
+
*
|
|
1161
|
+
* @locks mcluster->lock MUST be held.
|
|
1162
|
+
*/
|
|
1163
|
+
static rd_kafka_topic_partition_list_t *
|
|
1164
|
+
rd_kafka_mock_cgrp_consumer_member_needs_revocation(
|
|
1165
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1166
|
+
rd_kafka_topic_partition_list_t *intersection;
|
|
1167
|
+
rd_bool_t needs_revocation;
|
|
1168
|
+
|
|
1169
|
+
if (member->current_assignment)
|
|
1170
|
+
/* If we have a current assignment we
|
|
1171
|
+
* calculate the intersection with
|
|
1172
|
+
* target assignment. */
|
|
1173
|
+
intersection = rd_kafka_topic_partition_list_intersection_by_id(
|
|
1174
|
+
member->current_assignment, member->target_assignment);
|
|
1175
|
+
else
|
|
1176
|
+
/* Otherwise intersection is empty. */
|
|
1177
|
+
intersection = rd_kafka_topic_partition_list_new(0);
|
|
1178
|
+
|
|
1179
|
+
needs_revocation = member->current_assignment &&
|
|
1180
|
+
intersection->cnt < member->current_assignment->cnt;
|
|
1181
|
+
if (needs_revocation) {
|
|
1182
|
+
return intersection;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
rd_kafka_topic_partition_list_destroy(intersection);
|
|
1186
|
+
return NULL;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* @brief Calculates if \p member,
|
|
1191
|
+
* can receive new partitions, given revocation is completed.
|
|
1192
|
+
* In case new partitions aren't held by other members it
|
|
1193
|
+
* returns the assignable target assignment and bumps current
|
|
1194
|
+
* member epoch, otherwise it returns NULL and
|
|
1195
|
+
* doesn't change current member epoch.
|
|
1196
|
+
*
|
|
1197
|
+
* @param member The group member.
|
|
1198
|
+
*
|
|
1199
|
+
* @return The assignable set of partitions, or NULL in case new partitions
|
|
1200
|
+
* cannot be assigned yet.
|
|
1201
|
+
*
|
|
1202
|
+
* @remark The returned pointer ownership is transferred to the caller.
|
|
1203
|
+
*
|
|
1204
|
+
* @locks mcluster->lock MUST be held.
|
|
1205
|
+
*/
|
|
1206
|
+
static rd_kafka_topic_partition_list_t *
|
|
1207
|
+
rd_kafka_mock_cgrp_consumer_member_needs_assignment(
|
|
1208
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1209
|
+
rd_kafka_topic_partition_list_t *returned_assignment =
|
|
1210
|
+
rd_kafka_mock_cgrp_consumer_member_target_assignment_assignable(
|
|
1211
|
+
member);
|
|
1212
|
+
|
|
1213
|
+
if (!rd_kafka_mock_cgrp_consumer_member_next_assignment_can_bump_epoch(
|
|
1214
|
+
member, returned_assignment)) {
|
|
1215
|
+
/* We can't bump the epoch still,
|
|
1216
|
+
* there are some partitions held by other members.
|
|
1217
|
+
* We have to return NULL. */
|
|
1218
|
+
rd_kafka_topic_partition_list_destroy(returned_assignment);
|
|
1219
|
+
return NULL;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/* No partitions to remove, return
|
|
1223
|
+
* target assignment and reconcile the
|
|
1224
|
+
* epochs */
|
|
1225
|
+
member->current_member_epoch = member->target_member_epoch;
|
|
1226
|
+
return returned_assignment;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
/**
|
|
1230
|
+
* @brief Calculates next assignment and member epoch for a \p member,
|
|
1231
|
+
* given \p current_assignment.
|
|
1232
|
+
*
|
|
1233
|
+
* @param member The group member.
|
|
1234
|
+
* @param current_assignment The assignment sent by the member, or NULL if it
|
|
1235
|
+
* didn't change. Must be NULL if *member_epoch is 0.
|
|
1236
|
+
* @param member_epoch Pointer to client reported member epoch. Can be updated.
|
|
1237
|
+
*
|
|
1238
|
+
* @return The new assignment to return to the member.
|
|
1239
|
+
*
|
|
1240
|
+
* @remark The returned pointer ownership is transferred to the caller.
|
|
1241
|
+
*
|
|
1242
|
+
* @locks mcluster->lock MUST be held.
|
|
1243
|
+
*/
|
|
1244
|
+
rd_kafka_topic_partition_list_t *
|
|
1245
|
+
rd_kafka_mock_cgrp_consumer_member_next_assignment(
|
|
1246
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1247
|
+
rd_kafka_topic_partition_list_t *current_assignment,
|
|
1248
|
+
int *member_epoch) {
|
|
1249
|
+
rd_kafka_topic_partition_list_t *assignment_to_return = NULL;
|
|
1250
|
+
|
|
1251
|
+
if (current_assignment) {
|
|
1252
|
+
/* Update current assignment to reflect what is provided
|
|
1253
|
+
* by the client. */
|
|
1254
|
+
rd_kafka_mock_cgrp_consumer_member_current_assignment_set(
|
|
1255
|
+
member, current_assignment);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
if (*member_epoch > 0 &&
|
|
1259
|
+
member->current_member_epoch != *member_epoch) {
|
|
1260
|
+
/* Member epoch is different from the one we expect,
|
|
1261
|
+
* that means we have to fence the member. */
|
|
1262
|
+
*member_epoch = -1; /* FENCED_MEMBER_EPOCH */
|
|
1263
|
+
return NULL;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
if (member->target_assignment) {
|
|
1267
|
+
/* We have a target assignment,
|
|
1268
|
+
* let's check if we can assign it. */
|
|
1269
|
+
|
|
1270
|
+
if (*member_epoch != member->current_member_epoch ||
|
|
1271
|
+
member->current_member_epoch !=
|
|
1272
|
+
member->target_member_epoch) {
|
|
1273
|
+
/* Epochs are different, that means we have to bump the
|
|
1274
|
+
* epoch immediately or do some revocations
|
|
1275
|
+
* before that. */
|
|
1276
|
+
|
|
1277
|
+
assignment_to_return =
|
|
1278
|
+
rd_kafka_mock_cgrp_consumer_member_needs_revocation(
|
|
1279
|
+
member);
|
|
1280
|
+
if (!assignment_to_return) {
|
|
1281
|
+
/* After revocation we only have to
|
|
1282
|
+
* add new partitions.
|
|
1283
|
+
* In case these new partitions are held
|
|
1284
|
+
* by other members we still cannot do it. */
|
|
1285
|
+
assignment_to_return =
|
|
1286
|
+
rd_kafka_mock_cgrp_consumer_member_needs_assignment(
|
|
1287
|
+
member);
|
|
1288
|
+
}
|
|
1289
|
+
} else if (!member->returned_assignment) {
|
|
1290
|
+
/* If all the epochs are the same, the only case
|
|
1291
|
+
* where we have to return the assignment is
|
|
1292
|
+
* after a disconnection, when returned_assignment has
|
|
1293
|
+
* been reset to NULL. */
|
|
1294
|
+
assignment_to_return =
|
|
1295
|
+
rd_kafka_mock_cgrp_consumer_member_target_assignment_assignable(
|
|
1296
|
+
member);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
*member_epoch = member->current_member_epoch;
|
|
1301
|
+
if (assignment_to_return) {
|
|
1302
|
+
/* Compare assignment_to_return with last returned_assignment.
|
|
1303
|
+
* If equal, return NULL, otherwise return assignment_to_return
|
|
1304
|
+
* and update last returned_assignment. */
|
|
1305
|
+
rd_bool_t same_returned_assignment =
|
|
1306
|
+
member->returned_assignment &&
|
|
1307
|
+
!rd_kafka_topic_partition_list_cmp(
|
|
1308
|
+
member->returned_assignment, assignment_to_return,
|
|
1309
|
+
rd_kafka_topic_partition_by_id_cmp);
|
|
1310
|
+
|
|
1311
|
+
if (same_returned_assignment) {
|
|
1312
|
+
/* Returned assignment is the same as previous
|
|
1313
|
+
* one, we return NULL instead to show no change. */
|
|
1314
|
+
rd_kafka_topic_partition_list_destroy(
|
|
1315
|
+
assignment_to_return);
|
|
1316
|
+
assignment_to_return = NULL;
|
|
1317
|
+
} else {
|
|
1318
|
+
/* We store returned assignment
|
|
1319
|
+
* for later comparison. */
|
|
1320
|
+
rd_kafka_mock_cgrp_consumer_member_returned_assignment_set(
|
|
1321
|
+
member, assignment_to_return);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return assignment_to_return;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* @brief Mark member as active (restart session timer).
|
|
1329
|
+
*
|
|
1330
|
+
* @param mcgrp Member's consumer group.
|
|
1331
|
+
* @param member Member to set as active.
|
|
1332
|
+
*
|
|
1333
|
+
* @locks mcluster->lock MUST be held.
|
|
1334
|
+
*/
|
|
1335
|
+
void rd_kafka_mock_cgrp_consumer_member_active(
|
|
1336
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1337
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1338
|
+
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
1339
|
+
"Marking mock consumer group member %s as active",
|
|
1340
|
+
member->id);
|
|
1341
|
+
member->ts_last_activity = rd_clock();
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* @brief Finds a member in consumer group \p mcgrp by \p MemberId.
|
|
1346
|
+
*
|
|
1347
|
+
* @param mcgrp Consumer group to search.
|
|
1348
|
+
* @param MemberId Member id to look for.
|
|
1349
|
+
* @return Found member or NULL.
|
|
1350
|
+
*
|
|
1351
|
+
* @locks mcluster->lock MUST be held.
|
|
1352
|
+
*/
|
|
1353
|
+
rd_kafka_mock_cgrp_consumer_member_t *rd_kafka_mock_cgrp_consumer_member_find(
|
|
1354
|
+
const rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1355
|
+
const rd_kafkap_str_t *MemberId) {
|
|
1356
|
+
const rd_kafka_mock_cgrp_consumer_member_t *member;
|
|
1357
|
+
TAILQ_FOREACH(member, &mcgrp->members, link) {
|
|
1358
|
+
if (!rd_kafkap_str_cmp_str(MemberId, member->id))
|
|
1359
|
+
return (rd_kafka_mock_cgrp_consumer_member_t *)member;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
return NULL;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* @brief Finds a member in consumer group \p mcgrp by \p InstanceId.
|
|
1367
|
+
*
|
|
1368
|
+
* @param mcgrp Consumer group to search.
|
|
1369
|
+
* @param InstanceId Instance id to look for.
|
|
1370
|
+
* @return Found member or NULL.
|
|
1371
|
+
*
|
|
1372
|
+
* @locks mcluster->lock MUST be held.
|
|
1373
|
+
*/
|
|
1374
|
+
rd_kafka_mock_cgrp_consumer_member_t *
|
|
1375
|
+
rd_kafka_mock_cgrp_consumer_member_find_by_instance_id(
|
|
1376
|
+
const rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1377
|
+
const rd_kafkap_str_t *InstanceId) {
|
|
1378
|
+
if (RD_KAFKAP_STR_IS_NULL(InstanceId))
|
|
1379
|
+
return NULL;
|
|
1380
|
+
|
|
1381
|
+
const rd_kafka_mock_cgrp_consumer_member_t *member;
|
|
1382
|
+
TAILQ_FOREACH(member, &mcgrp->members, link) {
|
|
1383
|
+
if (!member->instance_id)
|
|
1384
|
+
continue;
|
|
1385
|
+
|
|
1386
|
+
if (!rd_kafkap_str_cmp_str(InstanceId, member->instance_id))
|
|
1387
|
+
return (rd_kafka_mock_cgrp_consumer_member_t *)member;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
return NULL;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
static void validate_subscription(const rd_kafkap_str_t *SubscribedTopicNames,
|
|
1394
|
+
int32_t SubscribedTopicNamesCnt,
|
|
1395
|
+
const rd_kafkap_str_t *SubscribedTopicRegex) {
|
|
1396
|
+
/* Either they are both NULL
|
|
1397
|
+
* or both non-NULL. */
|
|
1398
|
+
rd_assert((SubscribedTopicNames == NULL) ==
|
|
1399
|
+
RD_KAFKAP_STR_IS_NULL(SubscribedTopicRegex));
|
|
1400
|
+
/* If they're not NULL at least one should be non-empty */
|
|
1401
|
+
rd_assert(SubscribedTopicNames == NULL || SubscribedTopicNamesCnt > 0 ||
|
|
1402
|
+
RD_KAFKAP_STR_LEN(SubscribedTopicRegex) > 0);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* @brief Set the subscribed topics for the member \p member based on \p
|
|
1407
|
+
* SubscribedTopicNames and \p SubscribedTopicRegex. Deduplicates the list after
|
|
1408
|
+
* sorting it.
|
|
1409
|
+
* @return `rd_true` if the subscription was changed, that happens
|
|
1410
|
+
* if it's set and different from previous one.
|
|
1411
|
+
*
|
|
1412
|
+
* @locks mcluster->lock MUST be held.
|
|
1413
|
+
*/
|
|
1414
|
+
static rd_bool_t rd_kafka_mock_cgrp_consumer_member_subscribed_topic_names_set(
|
|
1415
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1416
|
+
rd_kafkap_str_t *SubscribedTopicNames,
|
|
1417
|
+
int32_t SubscribedTopicNamesCnt,
|
|
1418
|
+
const rd_kafkap_str_t *SubscribedTopicRegex) {
|
|
1419
|
+
rd_bool_t changed = rd_false;
|
|
1420
|
+
rd_list_t *new_subscription;
|
|
1421
|
+
int32_t i;
|
|
1422
|
+
|
|
1423
|
+
validate_subscription(SubscribedTopicNames, SubscribedTopicNamesCnt,
|
|
1424
|
+
SubscribedTopicRegex);
|
|
1425
|
+
|
|
1426
|
+
if (!SubscribedTopicNames &&
|
|
1427
|
+
RD_KAFKAP_STR_IS_NULL(SubscribedTopicRegex) &&
|
|
1428
|
+
!member->subscribed_topic_regex) {
|
|
1429
|
+
/* When client is sending NULL for SubscribedTopicNames and
|
|
1430
|
+
* SubscribedTopicRegex, its subscription didn't change. If we
|
|
1431
|
+
* already had a regex, we need to compute the regex again. */
|
|
1432
|
+
return changed;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
if (SubscribedTopicNames) {
|
|
1436
|
+
RD_IF_FREE(member->subscribed_topic_names, rd_list_destroy);
|
|
1437
|
+
member->subscribed_topic_names =
|
|
1438
|
+
rd_list_new(SubscribedTopicNamesCnt, rd_free);
|
|
1439
|
+
for (i = 0; i < SubscribedTopicNamesCnt; i++) {
|
|
1440
|
+
rd_list_add(
|
|
1441
|
+
member->subscribed_topic_names,
|
|
1442
|
+
RD_KAFKAP_STR_DUP(&SubscribedTopicNames[i]));
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
if (!RD_KAFKAP_STR_IS_NULL(SubscribedTopicRegex)) {
|
|
1447
|
+
RD_IF_FREE(member->subscribed_topic_regex, rd_free);
|
|
1448
|
+
member->subscribed_topic_regex =
|
|
1449
|
+
RD_KAFKAP_STR_DUP(SubscribedTopicRegex);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
new_subscription =
|
|
1453
|
+
rd_list_new(rd_list_cnt(member->subscribed_topic_names), rd_free);
|
|
1454
|
+
|
|
1455
|
+
rd_list_copy_to(new_subscription, member->subscribed_topic_names,
|
|
1456
|
+
rd_list_string_copy, NULL);
|
|
1457
|
+
|
|
1458
|
+
if (member->subscribed_topic_regex[0]) {
|
|
1459
|
+
rd_kafka_mock_cluster_t *mcluster = member->mcgrp->cluster;
|
|
1460
|
+
rd_kafka_mock_topic_t *mtopic;
|
|
1461
|
+
char errstr[1];
|
|
1462
|
+
rd_regex_t *re = rd_regex_comp(member->subscribed_topic_regex,
|
|
1463
|
+
errstr, sizeof(errstr));
|
|
1464
|
+
|
|
1465
|
+
TAILQ_FOREACH(mtopic, &mcluster->topics, link) {
|
|
1466
|
+
if (rd_regex_exec(re, mtopic->name))
|
|
1467
|
+
rd_list_add(new_subscription,
|
|
1468
|
+
rd_strdup(mtopic->name));
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
rd_regex_destroy(re);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
rd_list_deduplicate(&new_subscription, rd_strcmp2);
|
|
1475
|
+
|
|
1476
|
+
if (!member->subscribed_topics ||
|
|
1477
|
+
rd_list_cmp(new_subscription, member->subscribed_topics,
|
|
1478
|
+
rd_list_cmp_str)) {
|
|
1479
|
+
if (member->subscribed_topics)
|
|
1480
|
+
rd_list_destroy(member->subscribed_topics);
|
|
1481
|
+
member->subscribed_topics =
|
|
1482
|
+
rd_list_copy(new_subscription, rd_list_string_copy, NULL);
|
|
1483
|
+
changed = rd_true;
|
|
1484
|
+
}
|
|
1485
|
+
rd_list_destroy(new_subscription);
|
|
1486
|
+
return changed;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
static void rd_kafka_mock_cgrp_consumer_member_topic_id_set(
|
|
1490
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1491
|
+
const rd_kafkap_str_t *MemberId) {
|
|
1492
|
+
/* KIP 1082: MemberId is generated by the client */
|
|
1493
|
+
rd_assert(RD_KAFKAP_STR_LEN(MemberId) > 0);
|
|
1494
|
+
RD_IF_FREE(member->id, rd_free);
|
|
1495
|
+
member->id = RD_KAFKAP_STR_DUP(MemberId);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* @brief Adds a member to consumer group \p mcgrp. If member with same
|
|
1500
|
+
* \p MemberId is already present, only updates the connection and
|
|
1501
|
+
* sets it as active.
|
|
1502
|
+
*
|
|
1503
|
+
* @param mcgrp Consumer group to add the member to.
|
|
1504
|
+
* @param conn Member connection.
|
|
1505
|
+
* @param MemberId Member id.
|
|
1506
|
+
* @param InstanceId Group instance id (optional).
|
|
1507
|
+
* @param session_timeout_ms Session timeout to use.
|
|
1508
|
+
* @param SubscribedTopicNames Array of subscribed topics.
|
|
1509
|
+
* Mandatory if the member is a new one.
|
|
1510
|
+
* @param SubscribedTopicNamesCnt Number of elements in \p SubscribedTopicNames.
|
|
1511
|
+
* @param SubscribedTopicRegex Subscribed topic regex.
|
|
1512
|
+
*
|
|
1513
|
+
* @return New or existing member, NULL if the member cannot be added.
|
|
1514
|
+
*
|
|
1515
|
+
* @locks mcluster->lock MUST be held.
|
|
1516
|
+
*/
|
|
1517
|
+
rd_kafka_mock_cgrp_consumer_member_t *rd_kafka_mock_cgrp_consumer_member_add(
|
|
1518
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1519
|
+
struct rd_kafka_mock_connection_s *conn,
|
|
1520
|
+
const rd_kafkap_str_t *MemberId,
|
|
1521
|
+
const rd_kafkap_str_t *InstanceId,
|
|
1522
|
+
rd_kafkap_str_t *SubscribedTopicNames,
|
|
1523
|
+
int32_t SubscribedTopicNamesCnt,
|
|
1524
|
+
const rd_kafkap_str_t *SubscribedTopicRegex) {
|
|
1525
|
+
rd_kafka_mock_cgrp_consumer_member_t *member = NULL;
|
|
1526
|
+
rd_bool_t changed = rd_false;
|
|
1527
|
+
|
|
1528
|
+
/* Find member */
|
|
1529
|
+
member = rd_kafka_mock_cgrp_consumer_member_find(mcgrp, MemberId);
|
|
1530
|
+
if (!member) {
|
|
1531
|
+
member = rd_kafka_mock_cgrp_consumer_member_find_by_instance_id(
|
|
1532
|
+
mcgrp, InstanceId);
|
|
1533
|
+
|
|
1534
|
+
if (member) {
|
|
1535
|
+
if (!member->left_static_membership) {
|
|
1536
|
+
/* Old member still active,
|
|
1537
|
+
* fence this one */
|
|
1538
|
+
return NULL;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if (rd_kafkap_str_cmp_str(MemberId, member->id) != 0) {
|
|
1542
|
+
/* Member is a new instance and is rejoining
|
|
1543
|
+
* with a new MemberId. */
|
|
1544
|
+
rd_kafka_mock_cgrp_consumer_member_topic_id_set(
|
|
1545
|
+
member, MemberId);
|
|
1546
|
+
}
|
|
1547
|
+
member->left_static_membership = rd_false;
|
|
1548
|
+
}
|
|
1549
|
+
} else {
|
|
1550
|
+
member->left_static_membership = rd_false;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
if (!member) {
|
|
1554
|
+
validate_subscription(SubscribedTopicNames,
|
|
1555
|
+
SubscribedTopicNamesCnt,
|
|
1556
|
+
SubscribedTopicRegex);
|
|
1557
|
+
|
|
1558
|
+
/* In case of session timeout
|
|
1559
|
+
* where the member isn't aware it's been fenced. */
|
|
1560
|
+
if (SubscribedTopicNames == NULL)
|
|
1561
|
+
return NULL;
|
|
1562
|
+
|
|
1563
|
+
/* Not found, add member */
|
|
1564
|
+
member = rd_calloc(1, sizeof(*member));
|
|
1565
|
+
member->mcgrp = mcgrp;
|
|
1566
|
+
|
|
1567
|
+
rd_kafka_mock_cgrp_consumer_member_topic_id_set(member,
|
|
1568
|
+
MemberId);
|
|
1569
|
+
|
|
1570
|
+
if (!RD_KAFKAP_STR_IS_NULL(InstanceId))
|
|
1571
|
+
member->instance_id = RD_KAFKAP_STR_DUP(InstanceId);
|
|
1572
|
+
|
|
1573
|
+
TAILQ_INSERT_TAIL(&mcgrp->members, member, link);
|
|
1574
|
+
mcgrp->member_cnt++;
|
|
1575
|
+
changed = rd_true;
|
|
1576
|
+
member->target_member_epoch = mcgrp->group_epoch;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
changed |=
|
|
1580
|
+
rd_kafka_mock_cgrp_consumer_member_subscribed_topic_names_set(
|
|
1581
|
+
member, SubscribedTopicNames, SubscribedTopicNamesCnt,
|
|
1582
|
+
SubscribedTopicRegex);
|
|
1583
|
+
|
|
1584
|
+
mcgrp->session_timeout_ms =
|
|
1585
|
+
mcgrp->cluster->defaults.group_consumer_session_timeout_ms;
|
|
1586
|
+
mcgrp->heartbeat_interval_ms =
|
|
1587
|
+
mcgrp->cluster->defaults.group_consumer_heartbeat_interval_ms;
|
|
1588
|
+
|
|
1589
|
+
member->conn = conn;
|
|
1590
|
+
|
|
1591
|
+
rd_kafka_mock_cgrp_consumer_member_active(mcgrp, member);
|
|
1592
|
+
|
|
1593
|
+
if (changed)
|
|
1594
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_recalculate(
|
|
1595
|
+
mcgrp);
|
|
1596
|
+
|
|
1597
|
+
return member;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/**
|
|
1601
|
+
* @brief Destroys a consumer group member, removing from its consumer group.
|
|
1602
|
+
*
|
|
1603
|
+
* @param mcgrp Member consumer group.
|
|
1604
|
+
* @param member Member to destroy.
|
|
1605
|
+
*
|
|
1606
|
+
* @locks mcluster->lock MUST be held.
|
|
1607
|
+
*/
|
|
1608
|
+
static void rd_kafka_mock_cgrp_consumer_member_destroy(
|
|
1609
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1610
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1611
|
+
rd_assert(mcgrp->member_cnt > 0);
|
|
1612
|
+
TAILQ_REMOVE(&mcgrp->members, member, link);
|
|
1613
|
+
mcgrp->member_cnt--;
|
|
1614
|
+
|
|
1615
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_recalculate(mcgrp);
|
|
1616
|
+
|
|
1617
|
+
rd_free(member->id);
|
|
1618
|
+
|
|
1619
|
+
if (member->instance_id)
|
|
1620
|
+
rd_free(member->instance_id);
|
|
1621
|
+
|
|
1622
|
+
RD_IF_FREE(member->target_assignment,
|
|
1623
|
+
rd_kafka_topic_partition_list_destroy);
|
|
1624
|
+
RD_IF_FREE(member->current_assignment,
|
|
1625
|
+
rd_kafka_topic_partition_list_destroy);
|
|
1626
|
+
RD_IF_FREE(member->returned_assignment,
|
|
1627
|
+
rd_kafka_topic_partition_list_destroy);
|
|
1628
|
+
RD_IF_FREE(member->subscribed_topics, rd_list_destroy_free);
|
|
1629
|
+
|
|
1630
|
+
RD_IF_FREE(member->subscribed_topic_names, rd_list_destroy_free);
|
|
1631
|
+
|
|
1632
|
+
RD_IF_FREE(member->subscribed_topic_regex, rd_free);
|
|
1633
|
+
|
|
1634
|
+
rd_free(member);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
static void rd_kafka_mock_cgrp_consumer_member_leave_static(
|
|
1638
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1639
|
+
member->left_static_membership = rd_true;
|
|
1640
|
+
rd_kafka_mock_cgrp_consumer_member_returned_assignment_set(member,
|
|
1641
|
+
NULL);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* @brief Called when a member must leave a consumer group.
|
|
1647
|
+
*
|
|
1648
|
+
* @param mcgrp Consumer group to leave.
|
|
1649
|
+
* @param member Member that leaves.
|
|
1650
|
+
* @param leave_static If true, the member is leaving with static group
|
|
1651
|
+
* membership.
|
|
1652
|
+
*
|
|
1653
|
+
* @locks mcluster->lock MUST be held.
|
|
1654
|
+
*/
|
|
1655
|
+
void rd_kafka_mock_cgrp_consumer_member_leave(
|
|
1656
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1657
|
+
rd_kafka_mock_cgrp_consumer_member_t *member,
|
|
1658
|
+
rd_bool_t leave_static) {
|
|
1659
|
+
rd_bool_t is_static = member->instance_id != NULL;
|
|
1660
|
+
|
|
1661
|
+
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
1662
|
+
"Member %s is leaving group %s, is static: %s, "
|
|
1663
|
+
"static leave: %s",
|
|
1664
|
+
member->id, mcgrp->id, RD_STR_ToF(is_static),
|
|
1665
|
+
RD_STR_ToF(leave_static));
|
|
1666
|
+
if (!is_static || !leave_static)
|
|
1667
|
+
rd_kafka_mock_cgrp_consumer_member_destroy(mcgrp, member);
|
|
1668
|
+
else
|
|
1669
|
+
rd_kafka_mock_cgrp_consumer_member_leave_static(member);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
/**
|
|
1673
|
+
* @brief Called when a member is fenced from a consumer group.
|
|
1674
|
+
*
|
|
1675
|
+
* @param mcgrp Consumer group.
|
|
1676
|
+
* @param member Member to fence.
|
|
1677
|
+
*
|
|
1678
|
+
* @locks mcluster->lock MUST be held.
|
|
1679
|
+
*/
|
|
1680
|
+
void rd_kafka_mock_cgrp_consumer_member_fenced(
|
|
1681
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp,
|
|
1682
|
+
rd_kafka_mock_cgrp_consumer_member_t *member) {
|
|
1683
|
+
|
|
1684
|
+
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
1685
|
+
"Member %s is fenced from group %s", member->id,
|
|
1686
|
+
mcgrp->id);
|
|
1687
|
+
|
|
1688
|
+
rd_kafka_mock_cgrp_consumer_member_destroy(mcgrp, member);
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* @brief Find a consumer group in cluster \p mcluster by \p GroupId.
|
|
1693
|
+
*
|
|
1694
|
+
* @param mcluster Cluster to search in.
|
|
1695
|
+
* @param GroupId Group id to search.
|
|
1696
|
+
* @return Found group or NULL.
|
|
1697
|
+
*
|
|
1698
|
+
* @locks mcluster->lock MUST be held.
|
|
1699
|
+
*/
|
|
1700
|
+
rd_kafka_mock_cgrp_consumer_t *
|
|
1701
|
+
rd_kafka_mock_cgrp_consumer_find(const rd_kafka_mock_cluster_t *mcluster,
|
|
1702
|
+
const rd_kafkap_str_t *GroupId) {
|
|
1703
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp;
|
|
1704
|
+
TAILQ_FOREACH(mcgrp, &mcluster->cgrps_consumer, link) {
|
|
1705
|
+
if (!rd_kafkap_str_cmp_str(GroupId, mcgrp->id))
|
|
1706
|
+
return mcgrp;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
return NULL;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
/**
|
|
1713
|
+
* @brief Check if any members have exceeded the session timeout.
|
|
1714
|
+
*
|
|
1715
|
+
* @param rkts Timers.
|
|
1716
|
+
* @param arg Consumer group.
|
|
1717
|
+
*
|
|
1718
|
+
* @locks mcluster->lock is acquired and released.
|
|
1719
|
+
*/
|
|
1720
|
+
static void rd_kafka_mock_cgrp_consumer_session_tmr_cb(rd_kafka_timers_t *rkts,
|
|
1721
|
+
void *arg) {
|
|
1722
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp = arg;
|
|
1723
|
+
rd_kafka_mock_cgrp_consumer_member_t *member, *tmp;
|
|
1724
|
+
rd_ts_t now = rd_clock();
|
|
1725
|
+
rd_kafka_mock_cluster_t *mcluster = mcgrp->cluster;
|
|
1726
|
+
|
|
1727
|
+
mtx_unlock(&mcluster->lock);
|
|
1728
|
+
TAILQ_FOREACH_SAFE(member, &mcgrp->members, link, tmp) {
|
|
1729
|
+
if (member->ts_last_activity +
|
|
1730
|
+
(mcgrp->session_timeout_ms * 1000) >
|
|
1731
|
+
now)
|
|
1732
|
+
continue;
|
|
1733
|
+
|
|
1734
|
+
rd_kafka_dbg(mcgrp->cluster->rk, MOCK, "MOCK",
|
|
1735
|
+
"Member %s session timed out for group %s",
|
|
1736
|
+
member->id, mcgrp->id);
|
|
1737
|
+
|
|
1738
|
+
rd_kafka_mock_cgrp_consumer_member_fenced(mcgrp, member);
|
|
1739
|
+
}
|
|
1740
|
+
mtx_unlock(&mcluster->lock);
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* @brief Find or create a "consumer" consumer group.
|
|
1746
|
+
*
|
|
1747
|
+
* @param mcluster Cluster to search in.
|
|
1748
|
+
* @param GroupId Group id to look for.
|
|
1749
|
+
* @return Found or new consumer group.
|
|
1750
|
+
*
|
|
1751
|
+
* @locks mcluster->lock MUST be held.
|
|
1752
|
+
*/
|
|
1753
|
+
rd_kafka_mock_cgrp_consumer_t *
|
|
1754
|
+
rd_kafka_mock_cgrp_consumer_get(rd_kafka_mock_cluster_t *mcluster,
|
|
1755
|
+
const rd_kafkap_str_t *GroupId) {
|
|
1756
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp;
|
|
1757
|
+
|
|
1758
|
+
mcgrp = rd_kafka_mock_cgrp_consumer_find(mcluster, GroupId);
|
|
1759
|
+
if (mcgrp)
|
|
1760
|
+
return mcgrp;
|
|
1761
|
+
|
|
1762
|
+
mcgrp = rd_calloc(1, sizeof(*mcgrp));
|
|
1763
|
+
mcgrp->cluster = mcluster;
|
|
1764
|
+
mcgrp->id = RD_KAFKAP_STR_DUP(GroupId);
|
|
1765
|
+
mcgrp->group_epoch = 1;
|
|
1766
|
+
TAILQ_INIT(&mcgrp->members);
|
|
1767
|
+
rd_kafka_timer_start(&mcluster->timers, &mcgrp->session_tmr,
|
|
1768
|
+
1000 * 1000 /*1s*/,
|
|
1769
|
+
rd_kafka_mock_cgrp_consumer_session_tmr_cb, mcgrp);
|
|
1770
|
+
|
|
1771
|
+
TAILQ_INSERT_TAIL(&mcluster->cgrps_consumer, mcgrp, link);
|
|
1772
|
+
|
|
1773
|
+
return mcgrp;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
|
|
1777
|
+
void rd_kafka_mock_cgrp_consumer_target_assignment(
|
|
1778
|
+
rd_kafka_mock_cluster_t *mcluster,
|
|
1779
|
+
const char *group_id,
|
|
1780
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_t *target_assignment) {
|
|
1781
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp;
|
|
1782
|
+
rd_kafkap_str_t *group_id_str =
|
|
1783
|
+
rd_kafkap_str_new(group_id, strlen(group_id));
|
|
1784
|
+
|
|
1785
|
+
mtx_lock(&mcluster->lock);
|
|
1786
|
+
|
|
1787
|
+
mcgrp = rd_kafka_mock_cgrp_consumer_find(mcluster, group_id_str);
|
|
1788
|
+
if (!mcgrp)
|
|
1789
|
+
goto destroy;
|
|
1790
|
+
|
|
1791
|
+
rd_kafka_mock_cgrp_consumer_target_assignment_set_manual(
|
|
1792
|
+
mcgrp, target_assignment);
|
|
1793
|
+
|
|
1794
|
+
destroy:
|
|
1795
|
+
rd_kafkap_str_destroy(group_id_str);
|
|
1796
|
+
mtx_unlock(&mcluster->lock);
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
void rd_kafka_mock_set_group_consumer_session_timeout_ms(
|
|
1800
|
+
rd_kafka_mock_cluster_t *mcluster,
|
|
1801
|
+
int group_consumer_session_timeout_ms) {
|
|
1802
|
+
mtx_lock(&mcluster->lock);
|
|
1803
|
+
mcluster->defaults.group_consumer_session_timeout_ms =
|
|
1804
|
+
group_consumer_session_timeout_ms;
|
|
1805
|
+
mtx_unlock(&mcluster->lock);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
void rd_kafka_mock_set_group_consumer_heartbeat_interval_ms(
|
|
1809
|
+
rd_kafka_mock_cluster_t *mcluster,
|
|
1810
|
+
int group_consumer_heartbeat_interval_ms) {
|
|
1811
|
+
mtx_lock(&mcluster->lock);
|
|
1812
|
+
mcluster->defaults.group_consumer_heartbeat_interval_ms =
|
|
1813
|
+
group_consumer_heartbeat_interval_ms;
|
|
1814
|
+
mtx_unlock(&mcluster->lock);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
/**
|
|
1818
|
+
* @brief A client connection closed, check if any consumer cgrp has any state
|
|
1819
|
+
* for this connection that needs to be cleared.
|
|
1820
|
+
*
|
|
1821
|
+
* @param mcluster Cluster to search in.
|
|
1822
|
+
* @param mconn Connection that was closed.
|
|
1823
|
+
*
|
|
1824
|
+
* @locks mcluster->lock MUST be held.
|
|
1825
|
+
*/
|
|
1826
|
+
void rd_kafka_mock_cgrps_consumer_connection_closed(
|
|
1827
|
+
rd_kafka_mock_cluster_t *mcluster,
|
|
1828
|
+
rd_kafka_mock_connection_t *mconn) {
|
|
1829
|
+
rd_kafka_mock_cgrp_consumer_t *mcgrp;
|
|
1830
|
+
|
|
1831
|
+
TAILQ_FOREACH(mcgrp, &mcluster->cgrps_consumer, link) {
|
|
1832
|
+
rd_kafka_mock_cgrp_consumer_member_t *member, *tmp;
|
|
1833
|
+
TAILQ_FOREACH_SAFE(member, &mcgrp->members, link, tmp) {
|
|
1834
|
+
if (member->conn == mconn) {
|
|
1835
|
+
member->conn = NULL;
|
|
1836
|
+
rd_kafka_mock_cgrp_consumer_member_returned_assignment_set(
|
|
1837
|
+
member, NULL);
|
|
1838
|
+
rd_kafka_mock_cgrp_consumer_member_current_assignment_set(
|
|
1839
|
+
member, NULL);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* @brief Destroys consumer group \p mcgrp and all of its members.
|
|
1847
|
+
*
|
|
1848
|
+
* @param mcgrp Consumer group to destroy.
|
|
1849
|
+
*
|
|
1850
|
+
* @locks mcluster->lock MUST be held.
|
|
1851
|
+
*/
|
|
1852
|
+
void rd_kafka_mock_cgrp_consumer_destroy(rd_kafka_mock_cgrp_consumer_t *mcgrp) {
|
|
1853
|
+
rd_kafka_mock_cgrp_consumer_member_t *member;
|
|
1854
|
+
|
|
1855
|
+
TAILQ_REMOVE(&mcgrp->cluster->cgrps_consumer, mcgrp, link);
|
|
1856
|
+
|
|
1857
|
+
rd_kafka_timer_stop(&mcgrp->cluster->timers, &mcgrp->session_tmr,
|
|
1858
|
+
rd_true);
|
|
1859
|
+
rd_free(mcgrp->id);
|
|
1860
|
+
while ((member = TAILQ_FIRST(&mcgrp->members)))
|
|
1861
|
+
rd_kafka_mock_cgrp_consumer_member_destroy(mcgrp, member);
|
|
1862
|
+
rd_free(mcgrp);
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
/**
|
|
1866
|
+
* @brief A client connection closed, check if any cgrp has any state
|
|
1867
|
+
* for this connection that needs to be cleared.
|
|
1868
|
+
*
|
|
1869
|
+
* @param mcluster Mock cluster.
|
|
1870
|
+
* @param mconn Connection that was closed.
|
|
1871
|
+
*/
|
|
1872
|
+
void rd_kafka_mock_cgrps_connection_closed(rd_kafka_mock_cluster_t *mcluster,
|
|
1873
|
+
rd_kafka_mock_connection_t *mconn) {
|
|
1874
|
+
rd_kafka_mock_cgrps_classic_connection_closed(mcluster, mconn);
|
|
1875
|
+
rd_kafka_mock_cgrps_consumer_connection_closed(mcluster, mconn);
|
|
1876
|
+
}
|