From 05de5b5668dba6c920d9a3da27d70a7d350ff89f Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Wed, 30 Sep 2020 12:54:31 +0100 Subject: [PATCH 01/34] REP-1233 fix for slow start up due to cpus restriction that was causing demo failures --- replicator-schema-translation/docker-compose.yml | 8 +------- .../dest_sasl_plain_auth/docker-compose.yml | 5 ----- replicator-security/dest_ssl_auth/docker-compose.yml | 5 ----- .../dest_ssl_encryption/docker-compose.yml | 5 ----- .../source_sasl_plain_auth/docker-compose.yml | 5 ----- replicator-security/source_ssl_auth/docker-compose.yml | 5 ----- .../source_ssl_encryption/docker-compose.yml | 5 ----- replicator-security/unsecure/docker-compose.yml | 4 ---- 8 files changed, 1 insertion(+), 41 deletions(-) diff --git a/replicator-schema-translation/docker-compose.yml b/replicator-schema-translation/docker-compose.yml index 58ae754e6..b7ff95e62 100644 --- a/replicator-schema-translation/docker-compose.yml +++ b/replicator-schema-translation/docker-compose.yml @@ -39,7 +39,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -60,7 +59,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -79,7 +77,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -110,7 +107,6 @@ services: srcSchemaregistry: image: ${REPOSITORY}/cp-schema-registry:${CONFLUENT_DOCKER_TAG} container_name: srcSchemaregistry - cpus: 0.4 restart: always depends_on: - srcKafka1 @@ -126,7 +122,6 @@ services: destSchemaregistry: image: ${REPOSITORY}/cp-schema-registry:${CONFLUENT_DOCKER_TAG} container_name: destSchemaregistry - cpus: 0.4 restart: always depends_on: - destKafka1 @@ -150,7 +145,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: kafka-client container_name: kafka-client - cpus: 0.1 depends_on: - srcKafka1 - connect @@ -161,7 +155,7 @@ services: cub kafka-ready -b srcKafka1:10091 1 60 --config /etc/kafka/kafka.properties && \ cub kafka-ready -b destKafka1:11091 1 60 --config /etc/kafka/kafka.properties && \ sleep 30 && \ - kafka-topics --bootstrap-server srcKafka:10091 --topic testTopic --create --replication-factor 1 --partitions 6 && \ + kafka-topics --bootstrap-server srcKafka1:10091 --topic testTopic --create --replication-factor 1 --partitions 6 && \ echo submitting test subjects && \ /etc/kafka/scripts/submit_source_subjects.sh && \ echo submitted source_subjects'" diff --git a/replicator-security/dest_sasl_plain_auth/docker-compose.yml b/replicator-security/dest_sasl_plain_auth/docker-compose.yml index bf98ded19..7f1c98840 100644 --- a/replicator-security/dest_sasl_plain_auth/docker-compose.yml +++ b/replicator-security/dest_sasl_plain_auth/docker-compose.yml @@ -44,7 +44,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -65,7 +64,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -89,7 +87,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -143,7 +140,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafkaClient container_name: srcKafkaClient - cpus: 0.1 depends_on: - srcKafka1 volumes: @@ -165,7 +161,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafkaClient container_name: destKafkaClient - cpus: 0.1 depends_on: - destKafka1 - connect diff --git a/replicator-security/dest_ssl_auth/docker-compose.yml b/replicator-security/dest_ssl_auth/docker-compose.yml index 9f6b8c903..b409351a2 100644 --- a/replicator-security/dest_ssl_auth/docker-compose.yml +++ b/replicator-security/dest_ssl_auth/docker-compose.yml @@ -39,7 +39,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -60,7 +59,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -88,7 +86,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -140,7 +137,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafkaClient container_name: srcKafkaClient - cpus: 0.1 depends_on: - srcKafka1 volumes: @@ -161,7 +157,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafkaClient container_name: destKafkaClient - cpus: 0.1 depends_on: - destKafka1 - connect diff --git a/replicator-security/dest_ssl_encryption/docker-compose.yml b/replicator-security/dest_ssl_encryption/docker-compose.yml index 979cd02c0..d87d46b12 100644 --- a/replicator-security/dest_ssl_encryption/docker-compose.yml +++ b/replicator-security/dest_ssl_encryption/docker-compose.yml @@ -39,7 +39,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -60,7 +59,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -86,7 +84,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -138,7 +135,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafkaClient container_name: srcKafkaClient - cpus: 0.1 depends_on: - srcKafka1 volumes: @@ -159,7 +155,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafkaClient container_name: destKafkaClient - cpus: 0.1 depends_on: - destKafka1 - connect diff --git a/replicator-security/source_sasl_plain_auth/docker-compose.yml b/replicator-security/source_sasl_plain_auth/docker-compose.yml index 7233186fc..dfad5b7ec 100644 --- a/replicator-security/source_sasl_plain_auth/docker-compose.yml +++ b/replicator-security/source_sasl_plain_auth/docker-compose.yml @@ -44,7 +44,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -70,7 +69,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -89,7 +87,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -128,7 +125,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafkaClient container_name: srcKafkaClient - cpus: 0.1 depends_on: - srcKafka1 volumes: @@ -157,7 +153,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafkaClient container_name: destKafkaClient - cpus: 0.1 depends_on: - destKafka1 - connect diff --git a/replicator-security/source_ssl_auth/docker-compose.yml b/replicator-security/source_ssl_auth/docker-compose.yml index 9ef60423d..5cbee96b3 100644 --- a/replicator-security/source_ssl_auth/docker-compose.yml +++ b/replicator-security/source_ssl_auth/docker-compose.yml @@ -39,7 +39,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -69,7 +68,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -88,7 +86,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -127,7 +124,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafkaClient container_name: srcKafkaClient - cpus: 0.1 depends_on: - srcKafka1 volumes: @@ -154,7 +150,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafkaClient container_name: destKafkaClient - cpus: 0.1 depends_on: - destKafka1 - connect diff --git a/replicator-security/source_ssl_encryption/docker-compose.yml b/replicator-security/source_ssl_encryption/docker-compose.yml index b238f2305..28296394a 100644 --- a/replicator-security/source_ssl_encryption/docker-compose.yml +++ b/replicator-security/source_ssl_encryption/docker-compose.yml @@ -39,7 +39,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -67,7 +66,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -86,7 +84,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -125,7 +122,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafkaClient container_name: srcKafkaClient - cpus: 0.1 depends_on: - srcKafka1 volumes: @@ -152,7 +148,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafkaClient container_name: destKafkaClient - cpus: 0.1 depends_on: - destKafka1 - connect diff --git a/replicator-security/unsecure/docker-compose.yml b/replicator-security/unsecure/docker-compose.yml index 5ed46ee2a..14b4831fa 100644 --- a/replicator-security/unsecure/docker-compose.yml +++ b/replicator-security/unsecure/docker-compose.yml @@ -39,7 +39,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: srcKafka1 container_name: srcKafka1 - cpus: 0.3 depends_on: - srcZookeeper ports: @@ -60,7 +59,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: destKafka1 container_name: destKafka1 - cpus: 0.3 depends_on: - destZookeeper ports: @@ -79,7 +77,6 @@ services: connect: image: ${REPOSITORY}/cp-server-connect:${CONFLUENT_DOCKER_TAG} container_name: connect - cpus: 0.2 restart: always ports: - "8083:8083" @@ -117,7 +114,6 @@ services: image: ${REPOSITORY}/cp-server:${CONFLUENT_DOCKER_TAG} hostname: kafka-client container_name: kafka-client - cpus: 0.1 depends_on: - srcKafka1 - connect From 52eb629b171c8f701b6e5c3b189e941527bda8f4 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Mon, 2 Nov 2020 13:05:28 +0000 Subject: [PATCH 02/34] Demo script for observer promotion --- ...lacement-leader-is-observer-promotion.json | 20 ++ .../placement-under-min-isr-promotion.json | 20 ++ .../placement-under-replicated-promotion.json | 20 ++ multiregion/observer-promotion-demos.txt | 175 ++++++++++++++++++ multiregion/scripts/start.sh | 2 +- utils/config.env | 12 +- 6 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 multiregion/config/placement-leader-is-observer-promotion.json create mode 100644 multiregion/config/placement-under-min-isr-promotion.json create mode 100644 multiregion/config/placement-under-replicated-promotion.json create mode 100644 multiregion/observer-promotion-demos.txt diff --git a/multiregion/config/placement-leader-is-observer-promotion.json b/multiregion/config/placement-leader-is-observer-promotion.json new file mode 100644 index 000000000..80002542b --- /dev/null +++ b/multiregion/config/placement-leader-is-observer-promotion.json @@ -0,0 +1,20 @@ +{ + "version": 2, + "replicas": [ + { + "count": 2, + "constraints": { + "rack": "west" + } + } + ], + "observers": [ + { + "count": 2, + "constraints": { + "rack": "east" + } + } + ], + "observerPromotionPolicy":"leader-is-observer" +} \ No newline at end of file diff --git a/multiregion/config/placement-under-min-isr-promotion.json b/multiregion/config/placement-under-min-isr-promotion.json new file mode 100644 index 000000000..e89408f7b --- /dev/null +++ b/multiregion/config/placement-under-min-isr-promotion.json @@ -0,0 +1,20 @@ +{ + "version": 2, + "replicas": [ + { + "count": 2, + "constraints": { + "rack": "west" + } + } + ], + "observers": [ + { + "count": 2, + "constraints": { + "rack": "east" + } + } + ], + "observerPromotionPolicy":"under-min-isr" +} diff --git a/multiregion/config/placement-under-replicated-promotion.json b/multiregion/config/placement-under-replicated-promotion.json new file mode 100644 index 000000000..47838821f --- /dev/null +++ b/multiregion/config/placement-under-replicated-promotion.json @@ -0,0 +1,20 @@ +{ + "version": 2, + "replicas": [ + { + "count": 2, + "constraints": { + "rack": "west" + } + } + ], + "observers": [ + { + "count": 2, + "constraints": { + "rack": "east" + } + } + ], + "observerPromotionPolicy":"under-replicated" +} \ No newline at end of file diff --git a/multiregion/observer-promotion-demos.txt b/multiregion/observer-promotion-demos.txt new file mode 100644 index 000000000..23f3191a6 --- /dev/null +++ b/multiregion/observer-promotion-demos.txt @@ -0,0 +1,175 @@ +Under-Min-Isr: + +1. Create /multiregion/config/placement-under-min-isr-promotion.json (/etc/kafka/demo/placement-under-min-isr-promotion.json): + +{ + "version": 2, + "replicas": [ + { + "count": 2, + "constraints": { + "rack": "west" + } + } + ], + "observers": [ + { + "count": 2, + "constraints": { + "rack": "east" + } + } + ], + "observerPromotionPolicy":"under-min-isr" +} + + +2. Create the topic + +kafka-topics --create \ + --bootstrap-server broker-west-1:19091 \ + --topic under-min-isr-promotion \ + --partitions 1 \ + --replica-placement /etc/kafka/demo/placement-under-min-isr-promotion.json \ + --config min.insync.replicas=2 + +3. Identify the leader + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-1:19091 --describe --topic under-min-isr-promotion +Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,4,3 Isr: 2,1 Offline: Observers: 4,3 + +4. Stop the one that isn't the leader + +docker-compose stop [if Leader=2 then broker-west-1 else broker-west-2] + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-min-isr-promotion +Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,4,3 Isr: 2,4 Offline: 1 Observers: 4,3 + +5. Bring the broker back up + +docker-compose up -d broker-west-1 + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-min-isr-promotion +Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,4,3 Isr: 2,1 Offline: Observers: 4,3 + + +Under-Replicated: + +1. Create /multiregion/config/placement-under-replicated-promotion.json (/etc/kafka/demo/placement-under-replicated-promotion.json): + +{ + "version": 2, + "replicas": [ + { + "count": 2, + "constraints": { + "rack": "west" + } + } + ], + "observers": [ + { + "count": 2, + "constraints": { + "rack": "east" + } + } + ], + "observerPromotionPolicy":"under-replicated" +} + + +2. Create the topic + +kafka-topics --create \ + --bootstrap-server broker-west-1:19091 \ + --topic under-replicated-promotion \ + --partitions 1 \ + --replica-placement /etc/kafka/demo/placement-under-replicated-promotion.json \ + --config min.insync.replicas=1 + +3. Identify the leader + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-replicated-promotion +Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + +4. Stop the one that isn't the leader + +docker-compose stop [if Leader=2 then broker-west-1 else broker-west-2] + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-replicated-promotion +Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 + +5. Bring the broker back up + +docker-compose up -d broker-west-1 + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-replicated-promotion +Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + + +Leader-Is-Observer: + +1. Create /multiregion/config/placement-leader-is-observer-promotion.json (/etc/kafka/demo/placement-leader-is-observer-promotion.json): + +{ + "version": 2, + "replicas": [ + { + "count": 2, + "constraints": { + "rack": "west" + } + } + ], + "observers": [ + { + "count": 2, + "constraints": { + "rack": "east" + } + } + ], + "observerPromotionPolicy":"leader-is-observer" +} + + +2. Create the topic + +kafka-topics --create \ + --bootstrap-server broker-west-1:19091 \ + --topic leader-is-observer-promotion \ + --partitions 1 \ + --replica-placement /etc/kafka/demo/placement-leader-is-observer-promotion.json \ + --config min.insync.replicas=1 + +3. Identify the leader + +[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic leader-is-observer-promotion +Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + +4. Stop the one that isn't the leader + +docker-compose stop [if Leader=2 then broker-west-1 else broker-west-2] + +[appuser@broker-west-1 ~]$ kafka-topics --bootstrap-server broker-west-1:19091 --describe --topic leader-is-observer-promotion +Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: leader-is-observer-promotion Partition: 0 Leader: 1 Replicas: 1,2,4,3 Isr: 1 Offline: 2 Observers: 4,3 + +# note no promotion happens + +5. Bring the broker back up + +docker-compose up -d broker-west-2 + +[appuser@broker-west-1 ~]$ kafka-topics --bootstrap-server broker-west-1:19091 --describe --topic leader-is-observer-promotion +Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: leader-is-observer-promotion Partition: 0 Leader: 1 Replicas: 1,2,4,3 Isr: 1,2 Offline: Observers: 4,3 + diff --git a/multiregion/scripts/start.sh b/multiregion/scripts/start.sh index 82ac3e866..da9474214 100755 --- a/multiregion/scripts/start.sh +++ b/multiregion/scripts/start.sh @@ -4,7 +4,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" source ${DIR}/../.env ${DIR}/stop.sh - +set -x # Confluent's ubi-based Docker images do not have 'tc' installed echo echo "Build custom cp-zookeeper and cp-server images with 'tc' installed" diff --git a/utils/config.env b/utils/config.env index 1e255e958..df3a9787a 100644 --- a/utils/config.env +++ b/utils/config.env @@ -5,13 +5,13 @@ # over time to construct other # values required by this repository ##################################################### -CONFLUENT=6.0.0 -CONFLUENT_DOCKER_TAG=6.0.0 -CONFLUENT_SHORT=6.0 +CONFLUENT=6.1.0 +CONFLUENT_DOCKER_TAG=6.1.x-latest +CONFLUENT_SHORT=6.1 CONFLUENT_PREVIOUS="" -CONFLUENT_RELEASE_TAG_OR_BRANCH=6.0.0-post +CONFLUENT_RELEASE_TAG_OR_BRANCH=6.1.0-post CONFLUENT_MAJOR=6 -CONFLUENT_MINOR=0 +CONFLUENT_MINOR=1 CONFLUENT_PATCH=0 ##################################################### @@ -19,7 +19,7 @@ CP_VERSION_FULL="$CONFLUENT_MAJOR.$CONFLUENT_MINOR.$CONFLUENT_PATCH" # REPOSITORY - repository for Docker image # The '/' which separates the REPOSITORY from the image name is not required here -REPOSITORY=confluentinc +REPOSITORY=368821881613.dkr.ecr.us-west-2.amazonaws.com/confluentinc ##################################################### # We use below values in both Docker and Makefile(s) From fd09332b68a1d156139e408a864017037d256121 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Wed, 4 Nov 2020 17:37:13 +0000 Subject: [PATCH 03/34] KC-1027 multiregion observer promotion demo and docs --- multiregion/docs/multiregion.rst | 199 ++++++++++++++++++++++++- multiregion/scripts/create-topics.sh | 30 ++++ multiregion/scripts/describe-topics.sh | 15 ++ multiregion/scripts/jmx_metrics.sh | 2 +- multiregion/scripts/run-consumer.sh | 21 +++ multiregion/scripts/run-producer.sh | 30 ++++ multiregion/scripts/start.sh | 15 +- 7 files changed, 307 insertions(+), 5 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 38bc5be89..4a45926ca 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -46,6 +46,16 @@ participate in the ISR list and can't become the leader if the current leader fails, but if a user manually changes leader assignment then they can participate in the ISR list. +``Observer Promotion`` is the process whereby an observer is promoted into the +ISR in certain degraded scenarios. This behaviour is controlled by the +``observerPromotionPolicy`` field in a topic's replica placement policy. It can +have values: +- under-min-isr: observers will be promoted if the isr size drops below the + topic's min.insync.replicas configuration. +- under-replicated: observers will be promoted if the isr size drops below the + configured count of replicas in the topic's replica placement policy. +- leader-is-observer: observers will only be promoted if the leader is an observer. + |Follower_Fetching| @@ -200,6 +210,7 @@ You could create all the topics by running the script :devx-examples:`create-top - Observers (async replicas) - ISR list - Use default placement contraints + - Promotion policy * - single-region - 1x west @@ -207,6 +218,7 @@ You could create all the topics by running the script :devx-examples:`create-top - n/a - {1,2} - no + - none * - multi-region-sync - 1x west @@ -214,6 +226,7 @@ You could create all the topics by running the script :devx-examples:`create-top - n/a - {1,2,3,4} - no + - none * - multi-region-async - 1x west @@ -221,6 +234,7 @@ You could create all the topics by running the script :devx-examples:`create-top - 2x east - {1,2} - no + - none * - multi-region-default - 1x west @@ -228,6 +242,31 @@ You could create all the topics by running the script :devx-examples:`create-top - 2x east - {1,2} - yes + - none + + * - under-min-isr-promotion + - 1x west + - 1x west + - 2x east + - {1,2} + - no + - under-min-isr + + * - under-replicated-promotion + - 1x west + - 1x west + - 2x east + - {1,2} + - no + - under-replicated + + * - leader-is-observer-promotion + - 1x west + - 1x west + - 2x east + - {1,2} + - no + - leader-is-observer #. Create the |ak| topic ``single-region``. @@ -261,6 +300,34 @@ You could create all the topics by running the script :devx-examples:`create-top .. literalinclude:: ../scripts/create-topics.sh :lines: 34-38 +#. Create the |ak| topic ``under-min-isr-promotion``. + + .. literalinclude:: ../scripts/create-topics.sh + :lines: 42-48 + + Here is the topic's replica placement policy :devx-examples:`placement-under-min-isr-promotion.json|multiregion/config/placement-under-min-isr-promotion.json`: + + .. literalinclude:: ../config/placement-under-min-isr-promotion.json + +#. Create the |ak| topic ``under-replicated-promotion``. + + .. literalinclude:: ../scripts/create-topics.sh + :lines: 52-58 + + Here is the topic's replica placement policy :devx-examples:`placement-under-replicated-promotion.json|multiregion/config/placement-under-replicated-promotion.json`: + + .. literalinclude:: ../config/placement-under-replicated-promotion.json + +#. Create the |ak| topic ``leader-is-observer-promotion``. + + .. literalinclude:: ../scripts/create-topics.sh + :lines: 62-68 + + Here is the topic's replica placement policy :devx-examples:`placement-leader-is-observer-promotion.json|multiregion/config/placement-leader-is-observer-promotion.json`: + + .. literalinclude:: ../config/placement-leader-is-observer-promotion.json + + #. View the topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: .. code-block:: bash @@ -291,10 +358,24 @@ You could create all the topics by running the script :devx-examples:`create-top Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + ==> Describe topic under-min-isr-promotion + + Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + + ==> Describe topic under-replicated-promotion + + Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + + ==> Describe topic leader-is-observer-promotion + + Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: leader-is-observer-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 #. Observe the following: - - The ``multi-region-async`` and ``multi-region-default`` topics have replicas + - The ``multi-region-async``, ``under-min-isr-promotion``, ``under-replicated-promotion``, ``leader-is-observer-promotion`` and ``multi-region-default`` topics have replicas across ``west`` and ``east`` regions, but only 1 and 2 are in the ISR, and 3 and 4 are observers. @@ -404,6 +485,8 @@ metrics. For a description of other relevant JMX metrics, see It reports the number of replicas in the ISR. - ``CaughtUpReplicasCount`` - In JMX the full object name is ``kafka.cluster:type=Partition,name=CaughtUpReplicasCount,topic=,partition=``. It reports the number of replicas that are consider caught up to the topic partition leader. Note that this may be greater than the size of the ISR as observers may be caught up but are not part of ISR. +- ``ObserversInIsrCount`` - In JMX the full object name is ``kafka.cluster:type=Partition,name=ObserversInIsrCount,topic=,partition=``. + It reports the number of observers that are currently promoted to the ISR. There is a script you can run to collect the JMX metrics from the command line, but the general form is: @@ -414,7 +497,7 @@ There is a script you can run to collect the JMX metrics from the command line, #. Run the script :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the - JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, and + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``ObserversInIsrCount`` and ``CaughtUpReplicasCount`` from each of the brokers: .. code-block:: bash @@ -431,6 +514,9 @@ There is a script you can run to collect the JMX metrics from the command line, multi-region-sync: 4 multi-region-async: 4 multi-region-default: 4 + under-min-isr-promotion: 4 + under-replicated-promotion: 4 + leader-is-observer-promotion: 4 ==> Monitor InSyncReplicasCount @@ -439,6 +525,9 @@ There is a script you can run to collect the JMX metrics from the command line, multi-region-sync: 4 multi-region-async: 2 multi-region-default: 2 + under-min-isr-promotion: 2 + under-replicated-promotion: 2 + leader-is-observer-promotion: 2 ==> Monitor CaughtUpReplicasCount @@ -447,11 +536,97 @@ There is a script you can run to collect the JMX metrics from the command line, multi-region-sync: 4 multi-region-async: 4 multi-region-default: 4 + under-min-isr-promotion: 4 + under-replicated-promotion: 4 + leader-is-observer-promotion: 4 Failover and Failback --------------------- +Degrade Region +~~~~~~~~~~~ + +In this section, you will simulate a broker failure in the ``west`` region. + +#. Run the following command to stop the Docker containers corresponding to the ``west`` region: + + .. code-block:: bash + + docker-compose stop broker-west-1 + +#. Verify the new topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: + + .. code-block:: bash + + ./scripts/describe-topics.sh + + You should see output similar to the following: + + .. code-block:: text + +#. Verify the new topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: + + .. code-block:: bash + + ./scripts/describe-topics.sh + + You should see output similar to the following: + + .. code-block:: text + + ==> Describe topic single-region + + Topic: single-region PartitionCount: 1 ReplicationFactor: 2 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[]} + Topic: single-region Partition: 0 Leader: 2 Replicas: 1,2 Isr: 2 Offline: 1 + + ==> Describe topic multi-region-sync + + Topic: multi-region-sync PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}},{"count":2,"constraints":{"rack":"east"}}],"observers":[]} + Topic: multi-region-sync Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2,3,4 Offline: 1 + + ==> Describe topic multi-region-async + + Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 1,2,4,3 Isr: 2 Offline: 1 Observers: 4,3 + + ==> Describe topic multi-region-default + + Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2 Offline: 1 Observers: 3,4 + + ==> Describe topic under-min-isr-promotion + + Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 + + ==> Describe topic under-replicated-promotion + + Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 + + ==> Describe topic leader-is-observer-promotion + + Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: leader-is-observer-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2 Offline: 1 Observers: 3,4 + +#. Observe the following: + + - In all topics except ``under-min-isr-promotion``, ``multi-region-sync`` and ``under-replicated-promotion`` + there is only 1 replica in the ISR. This is because replica placement dictated all replicas were in the ``west`` + region which has only 1 remaining live broker. + + - In the second scenario, the ``multi-region-sync`` topic maintained an ISR of 3 brokers. This is because it's + placement policy always allows for brokers from east to join the ISR. + + - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have placement policies that allow + observers to be promoted into the ISR. In the case of ``under-min-isr-promotion`` the number of non-observer + replicas (1) is less than the ``min.insync.replicas`` value (2). Observers are promoted to the ISR to meet the + ``min.insync.replicas`` requirement. In the case of ``under-replicated-promotion`` the number of online replicas + (1) is less than the intended number of non observer replicas from the replica placement (2). An observer is + promoted to fulfil this requirement. + + Fail Region ~~~~~~~~~~~ @@ -653,6 +828,21 @@ Now you will bring region ``west`` back online. Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"east"}}],"observers":[{"count":2,"constraints":{"rack":"west"}}]} Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 3,4,2,1 Isr: 3,4 Offline: Observers: 2,1 + ==> Describe topic under-min-isr-promotion + + Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 + + ==> Describe topic under-replicated-promotion + + Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 + + ==> Describe topic leader-is-observer-promotion + + Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: leader-is-observer-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + #. Observe the following: - All topics have leaders again, in particular ``single-region`` which lost its @@ -665,6 +855,11 @@ Now you will bring region ``west`` back online. - The leader for ``multi-region-default`` stayed in the ``east`` region because you performed a permanent failover. + - Any observers automatically promoted in ``under-min-isr-promotion`` and + ``under-replicated-promotion`` are automatically demoted once the ``west`` + region is restored. Note: Leader election is not required for this demotion + process, it will happen as soon as the failed region is restored. + .. note:: On failback from a failover to observers, any data that wasn't replicated to diff --git a/multiregion/scripts/create-topics.sh b/multiregion/scripts/create-topics.sh index 627c28bc7..e62d7c733 100755 --- a/multiregion/scripts/create-topics.sh +++ b/multiregion/scripts/create-topics.sh @@ -36,3 +36,33 @@ docker-compose exec broker-west-1 kafka-topics \ --bootstrap-server broker-west-1:19091 \ --topic multi-region-default \ --config min.insync.replicas=1 + +echo -e "\n==> Creating topic under-min-isr-promotion" + +docker-compose exec broker-west-1 kafka-topics \ + --create \ + --bootstrap-server broker-west-1:19091 \ + --topic under-min-isr-promotion \ + --partitions 1 \ + --replica-placement /etc/kafka/demo/placement-under-min-isr-promotion.json \ + --config min.insync.replicas=2 + +echo -e "\n==> Creating topic under-replicated-promotion" + +docker-compose exec broker-west-1 kafka-topics \ + --create \ + --bootstrap-server broker-west-1:19091 \ + --topic under-replicated-promotion \ + --partitions 1 \ + --replica-placement /etc/kafka/demo/placement-under-replicated-promotion.json \ + --config min.insync.replicas=1 + +echo -e "\n==> Creating topic leader-is-observer-promotion" + +docker-compose exec broker-west-1 kafka-topics \ + --create \ + --bootstrap-server broker-west-1:19091 \ + --topic leader-is-observer-promotion \ + --partitions 1 \ + --replica-placement /etc/kafka/demo/placement-leader-is-observer-promotion.json \ + --config min.insync.replicas=1 diff --git a/multiregion/scripts/describe-topics.sh b/multiregion/scripts/describe-topics.sh index 6a3331de6..42346e48b 100755 --- a/multiregion/scripts/describe-topics.sh +++ b/multiregion/scripts/describe-topics.sh @@ -19,3 +19,18 @@ echo -e "\n==> Describe topic multi-region-default\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-default + +echo -e "\n==> Describe topic under-min-isr-promotion\n" + +docker-compose exec broker-east-3 kafka-topics --describe \ + --bootstrap-server broker-east-3:19093 --topic under-min-isr-promotion + +echo -e "\n==> Describe topic under-replicated-promotion\n" + +docker-compose exec broker-east-3 kafka-topics --describe \ + --bootstrap-server broker-east-3:19093 --topic under-replicated-promotion + +echo -e "\n==> Describe topic leader-is-observer-promotion\n" + +docker-compose exec broker-east-3 kafka-topics --describe \ + --bootstrap-server broker-east-3:19093 --topic leader-is-observer-promotion \ No newline at end of file diff --git a/multiregion/scripts/jmx_metrics.sh b/multiregion/scripts/jmx_metrics.sh index 4b94869c7..5c49e75cd 100755 --- a/multiregion/scripts/jmx_metrics.sh +++ b/multiregion/scripts/jmx_metrics.sh @@ -5,7 +5,7 @@ do echo -e "\n\n==> Monitor $metric \n" - for topic in single-region multi-region-sync multi-region-async multi-region-default + for topic in single-region multi-region-sync multi-region-async multi-region-default under-min-isr-promotion under-replicated-promotion leader-is-observer-promotion do BW1=$(docker-compose exec broker-west-1 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8091/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) BW2=$(docker-compose exec broker-west-2 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8092/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) diff --git a/multiregion/scripts/run-consumer.sh b/multiregion/scripts/run-consumer.sh index 68463b15d..f62c6b2fa 100755 --- a/multiregion/scripts/run-consumer.sh +++ b/multiregion/scripts/run-consumer.sh @@ -16,3 +16,24 @@ docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region- --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config + +docker-compose exec broker-east-3 kafka-consumer-perf-test --topic under-min-isr-promotion \ + --messages 5000 \ + --threads 1 \ + --broker-list broker-west-1:19091,broker-east-3:19093 \ + --timeout 20000 \ + --consumer.config /etc/kafka/demo/consumer-east.config + +docker-compose exec broker-east-3 kafka-consumer-perf-test --topic under-replicated-promotion \ + --messages 5000 \ + --threads 1 \ + --broker-list broker-west-1:19091,broker-east-3:19093 \ + --timeout 20000 \ + --consumer.config /etc/kafka/demo/consumer-east.config + +docker-compose exec broker-east-3 kafka-consumer-perf-test --topic leader-is-observer-promotion \ + --messages 5000 \ + --threads 1 \ + --broker-list broker-west-1:19091,broker-east-3:19093 \ + --timeout 20000 \ + --consumer.config /etc/kafka/demo/consumer-east.config \ No newline at end of file diff --git a/multiregion/scripts/run-producer.sh b/multiregion/scripts/run-producer.sh index 43b163e2a..3d8448605 100755 --- a/multiregion/scripts/run-producer.sh +++ b/multiregion/scripts/run-producer.sh @@ -38,3 +38,33 @@ docker-compose exec broker-west-1 kafka-producer-perf-test --topic multi-region- bootstrap.servers=broker-west-1:19091,broker-east-3:19093 \ compression.type=none \ batch.size=8196 + +docker-compose exec broker-west-1 kafka-producer-perf-test --topic under-min-isr-promotion \ + --num-records 5000 \ + --record-size 5000 \ + --throughput -1 \ + --producer-props \ + acks=all \ + bootstrap.servers=broker-west-1:19091,broker-east-3:19093 \ + compression.type=none \ + batch.size=8196 + +docker-compose exec broker-west-1 kafka-producer-perf-test --topic under-replicated-promotion \ + --num-records 5000 \ + --record-size 5000 \ + --throughput -1 \ + --producer-props \ + acks=all \ + bootstrap.servers=broker-west-1:19091,broker-east-3:19093 \ + compression.type=none \ + batch.size=8196 + +docker-compose exec broker-west-1 kafka-producer-perf-test --topic leader-is-observer-promotion \ + --num-records 5000 \ + --record-size 5000 \ + --throughput -1 \ + --producer-props \ + acks=all \ + bootstrap.servers=broker-west-1:19091,broker-east-3:19093 \ + compression.type=none \ + batch.size=8196 \ No newline at end of file diff --git a/multiregion/scripts/start.sh b/multiregion/scripts/start.sh index da9474214..ec6bd5113 100755 --- a/multiregion/scripts/start.sh +++ b/multiregion/scripts/start.sh @@ -4,7 +4,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" source ${DIR}/../.env ${DIR}/stop.sh -set -x + # Confluent's ubi-based Docker images do not have 'tc' installed echo echo "Build custom cp-zookeeper and cp-server images with 'tc' installed" @@ -53,8 +53,19 @@ sleep 5 ${DIR}/jmx_metrics.sh +echo -e "\nDegrade west region" +docker-compose stop broker-west-1 + +echo "Sleeping 30 seconds" +sleep 30 + +${DIR}/describe-topics.sh + +echo "Sleeping 30 seconds" +sleep 30 + echo -e "\nFail west region" -docker-compose stop broker-west-1 broker-west-2 zookeeper-west +docker-compose stop broker-west-2 zookeeper-west echo "Sleeping 30 seconds" sleep 30 From 0c59f52131794355e8150452afa85e3ecc8a4b0c Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Wed, 4 Nov 2020 20:03:45 +0000 Subject: [PATCH 04/34] KC-1029 removed notes committed by mistake --- multiregion/observer-promotion-demos.txt | 175 ----------------------- 1 file changed, 175 deletions(-) delete mode 100644 multiregion/observer-promotion-demos.txt diff --git a/multiregion/observer-promotion-demos.txt b/multiregion/observer-promotion-demos.txt deleted file mode 100644 index 23f3191a6..000000000 --- a/multiregion/observer-promotion-demos.txt +++ /dev/null @@ -1,175 +0,0 @@ -Under-Min-Isr: - -1. Create /multiregion/config/placement-under-min-isr-promotion.json (/etc/kafka/demo/placement-under-min-isr-promotion.json): - -{ - "version": 2, - "replicas": [ - { - "count": 2, - "constraints": { - "rack": "west" - } - } - ], - "observers": [ - { - "count": 2, - "constraints": { - "rack": "east" - } - } - ], - "observerPromotionPolicy":"under-min-isr" -} - - -2. Create the topic - -kafka-topics --create \ - --bootstrap-server broker-west-1:19091 \ - --topic under-min-isr-promotion \ - --partitions 1 \ - --replica-placement /etc/kafka/demo/placement-under-min-isr-promotion.json \ - --config min.insync.replicas=2 - -3. Identify the leader - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-1:19091 --describe --topic under-min-isr-promotion -Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,4,3 Isr: 2,1 Offline: Observers: 4,3 - -4. Stop the one that isn't the leader - -docker-compose stop [if Leader=2 then broker-west-1 else broker-west-2] - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-min-isr-promotion -Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,4,3 Isr: 2,4 Offline: 1 Observers: 4,3 - -5. Bring the broker back up - -docker-compose up -d broker-west-1 - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-min-isr-promotion -Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,4,3 Isr: 2,1 Offline: Observers: 4,3 - - -Under-Replicated: - -1. Create /multiregion/config/placement-under-replicated-promotion.json (/etc/kafka/demo/placement-under-replicated-promotion.json): - -{ - "version": 2, - "replicas": [ - { - "count": 2, - "constraints": { - "rack": "west" - } - } - ], - "observers": [ - { - "count": 2, - "constraints": { - "rack": "east" - } - } - ], - "observerPromotionPolicy":"under-replicated" -} - - -2. Create the topic - -kafka-topics --create \ - --bootstrap-server broker-west-1:19091 \ - --topic under-replicated-promotion \ - --partitions 1 \ - --replica-placement /etc/kafka/demo/placement-under-replicated-promotion.json \ - --config min.insync.replicas=1 - -3. Identify the leader - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-replicated-promotion -Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - -4. Stop the one that isn't the leader - -docker-compose stop [if Leader=2 then broker-west-1 else broker-west-2] - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-replicated-promotion -Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 - -5. Bring the broker back up - -docker-compose up -d broker-west-1 - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic under-replicated-promotion -Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - - -Leader-Is-Observer: - -1. Create /multiregion/config/placement-leader-is-observer-promotion.json (/etc/kafka/demo/placement-leader-is-observer-promotion.json): - -{ - "version": 2, - "replicas": [ - { - "count": 2, - "constraints": { - "rack": "west" - } - } - ], - "observers": [ - { - "count": 2, - "constraints": { - "rack": "east" - } - } - ], - "observerPromotionPolicy":"leader-is-observer" -} - - -2. Create the topic - -kafka-topics --create \ - --bootstrap-server broker-west-1:19091 \ - --topic leader-is-observer-promotion \ - --partitions 1 \ - --replica-placement /etc/kafka/demo/placement-leader-is-observer-promotion.json \ - --config min.insync.replicas=1 - -3. Identify the leader - -[appuser@broker-west-2 ~]$ kafka-topics --bootstrap-server broker-west-2:19092 --describe --topic leader-is-observer-promotion -Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - -4. Stop the one that isn't the leader - -docker-compose stop [if Leader=2 then broker-west-1 else broker-west-2] - -[appuser@broker-west-1 ~]$ kafka-topics --bootstrap-server broker-west-1:19091 --describe --topic leader-is-observer-promotion -Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: leader-is-observer-promotion Partition: 0 Leader: 1 Replicas: 1,2,4,3 Isr: 1 Offline: 2 Observers: 4,3 - -# note no promotion happens - -5. Bring the broker back up - -docker-compose up -d broker-west-2 - -[appuser@broker-west-1 ~]$ kafka-topics --bootstrap-server broker-west-1:19091 --describe --topic leader-is-observer-promotion -Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: leader-is-observer-promotion Partition: 0 Leader: 1 Replicas: 1,2,4,3 Isr: 1,2 Offline: Observers: 4,3 - From c284f9aca437233aaeb85127a57c264f5459bb6b Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 09:44:12 +0000 Subject: [PATCH 05/34] KC-1029 revert env file --- utils/config.env | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/config.env b/utils/config.env index df3a9787a..280e40b19 100644 --- a/utils/config.env +++ b/utils/config.env @@ -5,11 +5,11 @@ # over time to construct other # values required by this repository ##################################################### -CONFLUENT=6.1.0 +CONFLUENT=6.1.0-0 CONFLUENT_DOCKER_TAG=6.1.x-latest CONFLUENT_SHORT=6.1 CONFLUENT_PREVIOUS="" -CONFLUENT_RELEASE_TAG_OR_BRANCH=6.1.0-post +CONFLUENT_RELEASE_TAG_OR_BRANCH=master CONFLUENT_MAJOR=6 CONFLUENT_MINOR=1 CONFLUENT_PATCH=0 @@ -19,7 +19,7 @@ CP_VERSION_FULL="$CONFLUENT_MAJOR.$CONFLUENT_MINOR.$CONFLUENT_PATCH" # REPOSITORY - repository for Docker image # The '/' which separates the REPOSITORY from the image name is not required here -REPOSITORY=368821881613.dkr.ecr.us-west-2.amazonaws.com/confluentinc +REPOSITORY=confluentinc ##################################################### # We use below values in both Docker and Makefile(s) From 0632027d1676f707855da0a7d5a1c19f72544e31 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 15:08:39 +0000 Subject: [PATCH 06/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 4a45926ca..f5e95eb07 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -51,9 +51,9 @@ ISR in certain degraded scenarios. This behaviour is controlled by the ``observerPromotionPolicy`` field in a topic's replica placement policy. It can have values: - under-min-isr: observers will be promoted if the isr size drops below the - topic's min.insync.replicas configuration. +topic's min.insync.replicas configuration. - under-replicated: observers will be promoted if the isr size drops below the - configured count of replicas in the topic's replica placement policy. +configured count of replicas in the topic's replica placement policy. - leader-is-observer: observers will only be promoted if the leader is an observer. |Follower_Fetching| From 6392d1a4901b999789d4a27e2bea897376a7c13c Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 15:13:31 +0000 Subject: [PATCH 07/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index f5e95eb07..fad4d7967 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -201,7 +201,7 @@ You could create all the topics by running the script :devx-examples:`create-top .. list-table:: - :widths: 20 15 20 20 10 15 + :widths: 18 10 16 16 10 10 18 :header-rows: 1 * - Topic name From 3da43f807d623f2435f572371d37b6da89349da3 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 15:42:27 +0000 Subject: [PATCH 08/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index fad4d7967..3de0e3d36 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -545,7 +545,7 @@ Failover and Failback --------------------- Degrade Region -~~~~~~~~~~~ +~~~~~~~~~~~~~~ In this section, you will simulate a broker failure in the ``west`` region. From c0a13c73ec85d8215e556cbe69d6ef3281b11d63 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 15:51:43 +0000 Subject: [PATCH 09/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 3de0e3d36..b787435d2 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -50,10 +50,9 @@ participate in the ISR list. ISR in certain degraded scenarios. This behaviour is controlled by the ``observerPromotionPolicy`` field in a topic's replica placement policy. It can have values: -- under-min-isr: observers will be promoted if the isr size drops below the -topic's min.insync.replicas configuration. -- under-replicated: observers will be promoted if the isr size drops below the -configured count of replicas in the topic's replica placement policy. + +- under-min-isr: observers will be promoted if the isr size drops below the topic's min.insync.replicas configuration. +- under-replicated: observers will be promoted if the isr size drops below the configured count of replicas in the topic's replica placement policy. - leader-is-observer: observers will only be promoted if the leader is an observer. |Follower_Fetching| From df0df2f22b11287b863f918fc59547d1c0452a92 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 15:59:39 +0000 Subject: [PATCH 10/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b787435d2..dd879b63f 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -46,6 +46,8 @@ participate in the ISR list and can't become the leader if the current leader fails, but if a user manually changes leader assignment then they can participate in the ISR list. +|Follower_Fetching| + ``Observer Promotion`` is the process whereby an observer is promoted into the ISR in certain degraded scenarios. This behaviour is controlled by the ``observerPromotionPolicy`` field in a topic's replica placement policy. It can @@ -55,9 +57,6 @@ have values: - under-replicated: observers will be promoted if the isr size drops below the configured count of replicas in the topic's replica placement policy. - leader-is-observer: observers will only be promoted if the leader is an observer. -|Follower_Fetching| - - Configuration -------------- From 812a55501ca8afe39fdd7cbc968bb32532967e5e Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 16:12:18 +0000 Subject: [PATCH 11/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index dd879b63f..62af5bc21 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -553,16 +553,6 @@ In this section, you will simulate a broker failure in the ``west`` region. docker-compose stop broker-west-1 -#. Verify the new topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: - - .. code-block:: bash - - ./scripts/describe-topics.sh - - You should see output similar to the following: - - .. code-block:: text - #. Verify the new topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: .. code-block:: bash From 120ac89c51c227f8c6c70aeaf8f376289322d4ac Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 5 Nov 2020 16:18:28 +0000 Subject: [PATCH 12/34] KC-1029 fixes from docs build --- multiregion/docs/multiregion.rst | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 62af5bc21..b36abfd59 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -656,6 +656,21 @@ In this section, you will simulate a region failure by bringing down the ``west` Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 + ==> Describe topic under-min-isr-promotion + + Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-min-isr-promotion Partition: 0 Leader: 4 Replicas: 2,1,4,3 Isr: 4,3 Offline: 2,1 Observers: 4,3 + + ==> Describe topic under-replicated-promotion + + Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: under-replicated-promotion Partition: 0 Leader: 4 Replicas: 1,2,3,4 Isr: 4,3 Offline: 1,2 Observers: 3,4 + + ==> Describe topic leader-is-observer-promotion + + Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: leader-is-observer-promotion Partition: 0 Leader: none Replicas: 1,2,4,3 Isr: 2 Offline: 1,2 Observers: 4,3 + #. Observe the following: - In the first scenario, the ``single-region`` topic has no leader, because @@ -666,12 +681,16 @@ In this section, you will simulate a region failure by bringing down the ``west` elected a new leader in ``east`` (for example, replica 3 in the previous output). Clients can failover to those replicas in the ``east`` region. - - In the last two scenarios, the ``multi-region-async`` and - ``multi-region-default`` topics have no leader, because they had only two - replicas in the ISR, both of which were in the ``west`` region and are now - down. The observers in the ``east`` region are not eligible to become + - The ``multi-region-async``, ``multi-region-default`` and + ``leader-is-observer-promotion`` topics have no leader, because they had + only two replicas in the ISR, both of which were in the ``west`` region and + are now down. The observers in the ``east`` region are not eligible to become leaders automatically because they were not in the ISR. + - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have + have promoted observers into the ISR and an observer has become the leader. + This is because their observerPromotionPolicy allows this. + Failover Observers ~~~~~~~~~~~~~~~~~~ @@ -845,7 +864,7 @@ Now you will bring region ``west`` back online. - Any observers automatically promoted in ``under-min-isr-promotion`` and ``under-replicated-promotion`` are automatically demoted once the ``west`` - region is restored. Note: Leader election is not required for this demotion + region is restored. Leader election is not required for this demotion process, it will happen as soon as the failed region is restored. .. note:: From 059c2c5cab685cdd5b408168c6ea40d971de0ec2 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Fri, 13 Nov 2020 17:12:36 +0000 Subject: [PATCH 13/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b36abfd59..3608ea8fd 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -53,7 +53,7 @@ ISR in certain degraded scenarios. This behaviour is controlled by the ``observerPromotionPolicy`` field in a topic's replica placement policy. It can have values: -- under-min-isr: observers will be promoted if the isr size drops below the topic's min.insync.replicas configuration. +- ``under-min-isr``: observers will be promoted if the ISR size drops below the topic's ``min.insync.replicas`` configuration. - under-replicated: observers will be promoted if the isr size drops below the configured count of replicas in the topic's replica placement policy. - leader-is-observer: observers will only be promoted if the leader is an observer. @@ -961,4 +961,3 @@ Additional Resources -------------------- - `Blog post: Multi-Region Clusters with Confluent Platform 5.4 `__ - From 9e24adee34e9517dd220abdfe6f6a89f5d4ebcff Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Fri, 13 Nov 2020 17:13:09 +0000 Subject: [PATCH 14/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 3608ea8fd..701c645bd 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -54,7 +54,7 @@ ISR in certain degraded scenarios. This behaviour is controlled by the have values: - ``under-min-isr``: observers will be promoted if the ISR size drops below the topic's ``min.insync.replicas`` configuration. -- under-replicated: observers will be promoted if the isr size drops below the configured count of replicas in the topic's replica placement policy. +- ``under-replicated``: observers will be promoted if the ISR size drops below the ``replicas-->count`` value in the topic's replica placement policy. - leader-is-observer: observers will only be promoted if the leader is an observer. From b0a0debed52266c5ea74d4551264ba2c3c382591 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Fri, 13 Nov 2020 17:18:17 +0000 Subject: [PATCH 15/34] KC-1029 fixes per pr review --- multiregion/docs/multiregion.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b36abfd59..6b4bebcc8 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -41,10 +41,10 @@ An ``Observer`` is a broker/replica that also has a copy of data for a given topic-partition, and consumers are allowed to read from them even though the *Observer* isn't the leader–this is known as “Follower Fetching”. However, the data is copied asynchronously from the leader such that a producer doesn't wait -on observers to get back an acknowledgement. By default, observers don't -participate in the ISR list and can't become the leader if the current leader -fails, but if a user manually changes leader assignment then they can -participate in the ISR list. +on observers to get back an acknowledgement. In non degraded states, observers don't +participate in the ISR list and won't become the leader. If a broker in the ISR +fails, they could be promoted to the ISR list automatically with +``Observer Promotion`` or by manual changes to leader assignment. |Follower_Fetching| @@ -55,7 +55,7 @@ have values: - under-min-isr: observers will be promoted if the isr size drops below the topic's min.insync.replicas configuration. - under-replicated: observers will be promoted if the isr size drops below the configured count of replicas in the topic's replica placement policy. -- leader-is-observer: observers will only be promoted if the leader is an observer. +- leader-is-observer: observers will only be promoted if the current partition leader is an observer. Configuration @@ -208,7 +208,7 @@ You could create all the topics by running the script :devx-examples:`create-top - Observers (async replicas) - ISR list - Use default placement contraints - - Promotion policy + - Observer Promotion policy * - single-region - 1x west @@ -608,11 +608,11 @@ In this section, you will simulate a broker failure in the ``west`` region. placement policy always allows for brokers from east to join the ISR. - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have placement policies that allow - observers to be promoted into the ISR. In the case of ``under-min-isr-promotion`` the number of non-observer - replicas (1) is less than the ``min.insync.replicas`` value (2). Observers are promoted to the ISR to meet the - ``min.insync.replicas`` requirement. In the case of ``under-replicated-promotion`` the number of online replicas - (1) is less than the intended number of non observer replicas from the replica placement (2). An observer is - promoted to fulfil this requirement. + observers to be automatically promoted into the ISR. In the case of ``under-min-isr-promotion`` the number of + non-observer replicas (1) is less than the ``min.insync.replicas`` value (2). Observers are promoted to the ISR + to meet the ``min.insync.replicas`` requirement. In the case of ``under-replicated-promotion`` the number of + online replicas (1) is less than the intended number of non observer replicas from the replica placement (2). An + observer is promoted to fulfill this requirement. Fail Region From 57c957ee9fc8dfb79ccf53aae2ff8bdbcfaf5dc9 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Fri, 13 Nov 2020 17:24:56 +0000 Subject: [PATCH 16/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b80a7ae2d..a7d7d54df 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -604,7 +604,7 @@ In this section, you will simulate a broker failure in the ``west`` region. there is only 1 replica in the ISR. This is because replica placement dictated all replicas were in the ``west`` region which has only 1 remaining live broker. - - In the second scenario, the ``multi-region-sync`` topic maintained an ISR of 3 brokers. This is because it's + - In the second scenario, the ``multi-region-sync`` topic maintained an ISR of 3 brokers. This is because its placement policy always allows for brokers from east to join the ISR. - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have placement policies that allow From 89a74e47dad1f090f9bc80deda06a6cb021c14ae Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Fri, 13 Nov 2020 17:25:47 +0000 Subject: [PATCH 17/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index a7d7d54df..862027ee6 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -688,7 +688,7 @@ In this section, you will simulate a region failure by bringing down the ``west` leaders automatically because they were not in the ISR. - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have - have promoted observers into the ISR and an observer has become the leader. + promoted observers into the ISR and an observer has become the leader. This is because their observerPromotionPolicy allows this. From bcc1018658ebea927d385e533d0786cb43228313 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Fri, 13 Nov 2020 17:26:23 +0000 Subject: [PATCH 18/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 862027ee6..005a8c52a 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -689,7 +689,7 @@ In this section, you will simulate a region failure by bringing down the ``west` - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have promoted observers into the ISR and an observer has become the leader. - This is because their observerPromotionPolicy allows this. + This is because their replica placement policy has set ``observerPromotionPolicy`` to allow this. Failover Observers From 6e23948b33b4093b14d7caebecc0519011b2d745 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 19 Nov 2020 15:12:57 +0000 Subject: [PATCH 19/34] KC-1029 fixes per pr review --- ...i-region-async-op-leader-is-observer.json} | 0 ...-multi-region-async-op-under-min-isr.json} | 0 ...lti-region-async-op-under-replicated.json} | 0 multiregion/docs/multiregion.rst | 153 +++++++++--------- multiregion/scripts/create-topics.sh | 18 +-- multiregion/scripts/describe-topics.sh | 12 +- multiregion/scripts/jmx_metrics.sh | 4 +- multiregion/scripts/run-consumer.sh | 6 +- multiregion/scripts/run-producer.sh | 6 +- 9 files changed, 104 insertions(+), 95 deletions(-) rename multiregion/config/{placement-leader-is-observer-promotion.json => placement-multi-region-async-op-leader-is-observer.json} (100%) rename multiregion/config/{placement-under-min-isr-promotion.json => placement-multi-region-async-op-under-min-isr.json} (100%) rename multiregion/config/{placement-under-replicated-promotion.json => placement-multi-region-async-op-under-replicated.json} (100%) diff --git a/multiregion/config/placement-leader-is-observer-promotion.json b/multiregion/config/placement-multi-region-async-op-leader-is-observer.json similarity index 100% rename from multiregion/config/placement-leader-is-observer-promotion.json rename to multiregion/config/placement-multi-region-async-op-leader-is-observer.json diff --git a/multiregion/config/placement-under-min-isr-promotion.json b/multiregion/config/placement-multi-region-async-op-under-min-isr.json similarity index 100% rename from multiregion/config/placement-under-min-isr-promotion.json rename to multiregion/config/placement-multi-region-async-op-under-min-isr.json diff --git a/multiregion/config/placement-under-replicated-promotion.json b/multiregion/config/placement-multi-region-async-op-under-replicated.json similarity index 100% rename from multiregion/config/placement-under-replicated-promotion.json rename to multiregion/config/placement-multi-region-async-op-under-replicated.json diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b80a7ae2d..0796458bb 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -242,7 +242,7 @@ You could create all the topics by running the script :devx-examples:`create-top - yes - none - * - under-min-isr-promotion + * - multi-region-async-op-under-min-isr - 1x west - 1x west - 2x east @@ -250,7 +250,7 @@ You could create all the topics by running the script :devx-examples:`create-top - no - under-min-isr - * - under-replicated-promotion + * - multi-region-async-op-under-replicated - 1x west - 1x west - 2x east @@ -258,7 +258,7 @@ You could create all the topics by running the script :devx-examples:`create-top - no - under-replicated - * - leader-is-observer-promotion + * - multi-region-async-op-leader-is-observer - 1x west - 1x west - 2x east @@ -298,32 +298,32 @@ You could create all the topics by running the script :devx-examples:`create-top .. literalinclude:: ../scripts/create-topics.sh :lines: 34-38 -#. Create the |ak| topic ``under-min-isr-promotion``. +#. Create the |ak| topic ``multi-region-async-op-under-min-isr``. .. literalinclude:: ../scripts/create-topics.sh :lines: 42-48 - Here is the topic's replica placement policy :devx-examples:`placement-under-min-isr-promotion.json|multiregion/config/placement-under-min-isr-promotion.json`: + Here is the topic's replica placement policy :devx-examples:`placement-multi-region-async-op-under-min-isr.json|multiregion/config/placement-multi-region-async-op-under-min-isr.json`: - .. literalinclude:: ../config/placement-under-min-isr-promotion.json + .. literalinclude:: ../config/placement-multi-region-async-op-under-min-isr.json -#. Create the |ak| topic ``under-replicated-promotion``. +#. Create the |ak| topic ``multi-region-async-op-under-replicated``. .. literalinclude:: ../scripts/create-topics.sh :lines: 52-58 - Here is the topic's replica placement policy :devx-examples:`placement-under-replicated-promotion.json|multiregion/config/placement-under-replicated-promotion.json`: + Here is the topic's replica placement policy :devx-examples:`placement-multi-region-async-op-under-replicated.json|multiregion/config/placement-multi-region-async-op-under-replicated.json`: - .. literalinclude:: ../config/placement-under-replicated-promotion.json + .. literalinclude:: ../config/placement-multi-region-async-op-under-replicated.json -#. Create the |ak| topic ``leader-is-observer-promotion``. +#. Create the |ak| topic ``multi-region-async-op-leader-is-observer``. .. literalinclude:: ../scripts/create-topics.sh :lines: 62-68 - Here is the topic's replica placement policy :devx-examples:`placement-leader-is-observer-promotion.json|multiregion/config/placement-leader-is-observer-promotion.json`: + Here is the topic's replica placement policy :devx-examples:`placement-multi-region-async-op-leader-is-observer.json|multiregion/config/placement-multi-region-async-op-leader-is-observer.json`: - .. literalinclude:: ../config/placement-leader-is-observer-promotion.json + .. literalinclude:: ../config/placement-multi-region-async-op-leader-is-observer.json #. View the topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: @@ -356,24 +356,24 @@ You could create all the topics by running the script :devx-examples:`create-top Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic under-min-isr-promotion + ==> Describe topic multi-region-async-op-under-min-isr - Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic under-replicated-promotion + ==> Describe topic multi-region-async-op-under-replicated - Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic leader-is-observer-promotion + ==> Describe topic multi-region-async-op-leader-is-observer - Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: leader-is-observer-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 #. Observe the following: - - The ``multi-region-async``, ``under-min-isr-promotion``, ``under-replicated-promotion``, ``leader-is-observer-promotion`` and ``multi-region-default`` topics have replicas + - The ``multi-region-async``, ``multi-region-async-op-under-min-isr``, ``multi-region-async-op-under-replicated``, ``multi-region-async-op-leader-is-observer`` and ``multi-region-default`` topics have replicas across ``west`` and ``east`` regions, but only 1 and 2 are in the ISR, and 3 and 4 are observers. @@ -512,9 +512,9 @@ There is a script you can run to collect the JMX metrics from the command line, multi-region-sync: 4 multi-region-async: 4 multi-region-default: 4 - under-min-isr-promotion: 4 - under-replicated-promotion: 4 - leader-is-observer-promotion: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 4 ==> Monitor InSyncReplicasCount @@ -523,9 +523,9 @@ There is a script you can run to collect the JMX metrics from the command line, multi-region-sync: 4 multi-region-async: 2 multi-region-default: 2 - under-min-isr-promotion: 2 - under-replicated-promotion: 2 - leader-is-observer-promotion: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 2 ==> Monitor CaughtUpReplicasCount @@ -534,20 +534,27 @@ There is a script you can run to collect the JMX metrics from the command line, multi-region-sync: 4 multi-region-async: 4 multi-region-default: 4 - under-min-isr-promotion: 4 - under-replicated-promotion: 4 - leader-is-observer-promotion: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 4 + ==> Monitor ObserversInIsrCount -Failover and Failback ---------------------- + single-region: 0 + multi-region-sync: 0 + multi-region-async: 0 + multi-region-default: 0 + multi-region-async-op-under-min-isr: 0 + multi-region-async-op-under-replicated: 0 + multi-region-async-op-leader-is-observer: 0 -Degrade Region -~~~~~~~~~~~~~~ -In this section, you will simulate a broker failure in the ``west`` region. +Degraded Region +--------------- -#. Run the following command to stop the Docker containers corresponding to the ``west`` region: +In this section, you will simulate a single broker failure in the ``west`` region. + +#. Run the following command to stop on of the broker Docker containers corresponding in the ``west`` region: .. code-block:: bash @@ -583,37 +590,39 @@ In this section, you will simulate a broker failure in the ``west`` region. Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2 Offline: 1 Observers: 3,4 - ==> Describe topic under-min-isr-promotion + ==> Describe topic multi-region-async-op-under-min-isr - Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 + Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 - ==> Describe topic under-replicated-promotion + ==> Describe topic multi-region-async-op-under-replicated - Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 + Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 - ==> Describe topic leader-is-observer-promotion + ==> Describe topic multi-region-async-op-leader-is-observer - Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: leader-is-observer-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2 Offline: 1 Observers: 3,4 + Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2 Offline: 1 Observers: 3,4 #. Observe the following: - - In all topics except ``under-min-isr-promotion``, ``multi-region-sync`` and ``under-replicated-promotion`` + - In all topics except ``multi-region-async-op-under-min-isr``, ``multi-region-sync`` and ``multi-region-async-op-under-replicated`` there is only 1 replica in the ISR. This is because replica placement dictated all replicas were in the ``west`` region which has only 1 remaining live broker. - In the second scenario, the ``multi-region-sync`` topic maintained an ISR of 3 brokers. This is because it's placement policy always allows for brokers from east to join the ISR. - - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have placement policies that allow - observers to be automatically promoted into the ISR. In the case of ``under-min-isr-promotion`` the number of + - The ``multi-region-async-op-under-min-isr`` and ``multi-region-async-op-under-replicated`` topics have placement policies that allow + observers to be automatically promoted into the ISR. In the case of ``multi-region-async-op-under-min-isr`` the number of non-observer replicas (1) is less than the ``min.insync.replicas`` value (2). Observers are promoted to the ISR - to meet the ``min.insync.replicas`` requirement. In the case of ``under-replicated-promotion`` the number of + to meet the ``min.insync.replicas`` requirement. In the case of ``multi-region-async-op-under-replicated`` the number of online replicas (1) is less than the intended number of non observer replicas from the replica placement (2). An observer is promoted to fulfill this requirement. +Failover and Failback +--------------------- Fail Region ~~~~~~~~~~~ @@ -656,20 +665,20 @@ In this section, you will simulate a region failure by bringing down the ``west` Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 - ==> Describe topic under-min-isr-promotion + ==> Describe topic multi-region-async-op-under-min-isr - Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 4 Replicas: 2,1,4,3 Isr: 4,3 Offline: 2,1 Observers: 4,3 + Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 4 Replicas: 2,1,4,3 Isr: 4,3 Offline: 2,1 Observers: 4,3 - ==> Describe topic under-replicated-promotion + ==> Describe topic multi-region-async-op-under-replicated - Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 4 Replicas: 1,2,3,4 Isr: 4,3 Offline: 1,2 Observers: 3,4 + Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 4 Replicas: 1,2,3,4 Isr: 4,3 Offline: 1,2 Observers: 3,4 - ==> Describe topic leader-is-observer-promotion + ==> Describe topic multi-region-async-op-leader-is-observer - Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: leader-is-observer-promotion Partition: 0 Leader: none Replicas: 1,2,4,3 Isr: 2 Offline: 1,2 Observers: 4,3 + Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: none Replicas: 1,2,4,3 Isr: 2 Offline: 1,2 Observers: 4,3 #. Observe the following: @@ -682,12 +691,12 @@ In this section, you will simulate a region failure by bringing down the ``west` output). Clients can failover to those replicas in the ``east`` region. - The ``multi-region-async``, ``multi-region-default`` and - ``leader-is-observer-promotion`` topics have no leader, because they had + ``multi-region-async-op-leader-is-observer`` topics have no leader, because they had only two replicas in the ISR, both of which were in the ``west`` region and are now down. The observers in the ``east`` region are not eligible to become leaders automatically because they were not in the ISR. - - The ``under-min-isr-promotion`` and ``under-replicated-promotion`` topics have + - The ``multi-region-async-op-under-min-isr`` and ``multi-region-async-op-under-replicated`` topics have have promoted observers into the ISR and an observer has become the leader. This is because their observerPromotionPolicy allows this. @@ -835,20 +844,20 @@ Now you will bring region ``west`` back online. Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"east"}}],"observers":[{"count":2,"constraints":{"rack":"west"}}]} Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 3,4,2,1 Isr: 3,4 Offline: Observers: 2,1 - ==> Describe topic under-min-isr-promotion + ==> Describe topic multi-region-async-op-under-min-isr - Topic: under-min-isr-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-min-isr-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 + Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 - ==> Describe topic under-replicated-promotion + ==> Describe topic multi-region-async-op-under-replicated - Topic: under-replicated-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: under-replicated-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 + Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 - ==> Describe topic leader-is-observer-promotion + ==> Describe topic multi-region-async-op-leader-is-observer - Topic: leader-is-observer-promotion PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: leader-is-observer-promotion Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 #. Observe the following: @@ -862,8 +871,8 @@ Now you will bring region ``west`` back online. - The leader for ``multi-region-default`` stayed in the ``east`` region because you performed a permanent failover. - - Any observers automatically promoted in ``under-min-isr-promotion`` and - ``under-replicated-promotion`` are automatically demoted once the ``west`` + - Any observers automatically promoted in ``multi-region-async-op-under-min-isr`` and + ``multi-region-async-op-under-replicated`` are automatically demoted once the ``west`` region is restored. Leader election is not required for this demotion process, it will happen as soon as the failed region is restored. diff --git a/multiregion/scripts/create-topics.sh b/multiregion/scripts/create-topics.sh index e62d7c733..ea75860bd 100755 --- a/multiregion/scripts/create-topics.sh +++ b/multiregion/scripts/create-topics.sh @@ -37,32 +37,32 @@ docker-compose exec broker-west-1 kafka-topics \ --topic multi-region-default \ --config min.insync.replicas=1 -echo -e "\n==> Creating topic under-min-isr-promotion" +echo -e "\n==> Creating topic multi-region-async-op-under-min-isr" docker-compose exec broker-west-1 kafka-topics \ --create \ --bootstrap-server broker-west-1:19091 \ - --topic under-min-isr-promotion \ + --topic multi-region-async-op-under-min-isr \ --partitions 1 \ - --replica-placement /etc/kafka/demo/placement-under-min-isr-promotion.json \ + --replica-placement /etc/kafka/demo/placement-multi-region-async-op-under-min-isr.json \ --config min.insync.replicas=2 -echo -e "\n==> Creating topic under-replicated-promotion" +echo -e "\n==> Creating topic multi-region-async-op-under-replicated" docker-compose exec broker-west-1 kafka-topics \ --create \ --bootstrap-server broker-west-1:19091 \ - --topic under-replicated-promotion \ + --topic multi-region-async-op-under-replicated \ --partitions 1 \ - --replica-placement /etc/kafka/demo/placement-under-replicated-promotion.json \ + --replica-placement /etc/kafka/demo/placement-multi-region-async-op-under-replicated.json \ --config min.insync.replicas=1 -echo -e "\n==> Creating topic leader-is-observer-promotion" +echo -e "\n==> Creating topic multi-region-async-op-leader-is-observer" docker-compose exec broker-west-1 kafka-topics \ --create \ --bootstrap-server broker-west-1:19091 \ - --topic leader-is-observer-promotion \ + --topic multi-region-async-op-leader-is-observer \ --partitions 1 \ - --replica-placement /etc/kafka/demo/placement-leader-is-observer-promotion.json \ + --replica-placement /etc/kafka/demo/placement-multi-region-async-op-leader-is-observer.json \ --config min.insync.replicas=1 diff --git a/multiregion/scripts/describe-topics.sh b/multiregion/scripts/describe-topics.sh index 42346e48b..1fe2bde54 100755 --- a/multiregion/scripts/describe-topics.sh +++ b/multiregion/scripts/describe-topics.sh @@ -20,17 +20,17 @@ echo -e "\n==> Describe topic multi-region-default\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-default -echo -e "\n==> Describe topic under-min-isr-promotion\n" +echo -e "\n==> Describe topic multi-region-async-op-under-min-isr\n" docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic under-min-isr-promotion + --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-under-min-isr -echo -e "\n==> Describe topic under-replicated-promotion\n" +echo -e "\n==> Describe topic multi-region-async-op-under-replicated\n" docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic under-replicated-promotion + --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-under-replicated -echo -e "\n==> Describe topic leader-is-observer-promotion\n" +echo -e "\n==> Describe topic multi-region-async-op-leader-is-observer\n" docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic leader-is-observer-promotion \ No newline at end of file + --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-leader-is-observer \ No newline at end of file diff --git a/multiregion/scripts/jmx_metrics.sh b/multiregion/scripts/jmx_metrics.sh index 5c49e75cd..93eda65ac 100755 --- a/multiregion/scripts/jmx_metrics.sh +++ b/multiregion/scripts/jmx_metrics.sh @@ -1,11 +1,11 @@ #!/bin/bash -for metric in ReplicasCount InSyncReplicasCount CaughtUpReplicasCount +for metric in ReplicasCount InSyncReplicasCount CaughtUpReplicasCount ObserversInIsrCount do echo -e "\n\n==> Monitor $metric \n" - for topic in single-region multi-region-sync multi-region-async multi-region-default under-min-isr-promotion under-replicated-promotion leader-is-observer-promotion + for topic in single-region multi-region-sync multi-region-async multi-region-default multi-region-async-op-under-min-isr multi-region-async-op-under-replicated multi-region-async-op-leader-is-observer do BW1=$(docker-compose exec broker-west-1 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8091/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) BW2=$(docker-compose exec broker-west-2 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8092/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) diff --git a/multiregion/scripts/run-consumer.sh b/multiregion/scripts/run-consumer.sh index f62c6b2fa..9ad62348c 100755 --- a/multiregion/scripts/run-consumer.sh +++ b/multiregion/scripts/run-consumer.sh @@ -17,21 +17,21 @@ docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region- --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config -docker-compose exec broker-east-3 kafka-consumer-perf-test --topic under-min-isr-promotion \ +docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-under-min-isr \ --messages 5000 \ --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config -docker-compose exec broker-east-3 kafka-consumer-perf-test --topic under-replicated-promotion \ +docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-under-replicated \ --messages 5000 \ --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config -docker-compose exec broker-east-3 kafka-consumer-perf-test --topic leader-is-observer-promotion \ +docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-leader-is-observer \ --messages 5000 \ --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ diff --git a/multiregion/scripts/run-producer.sh b/multiregion/scripts/run-producer.sh index 3d8448605..78d43ad97 100755 --- a/multiregion/scripts/run-producer.sh +++ b/multiregion/scripts/run-producer.sh @@ -39,7 +39,7 @@ docker-compose exec broker-west-1 kafka-producer-perf-test --topic multi-region- compression.type=none \ batch.size=8196 -docker-compose exec broker-west-1 kafka-producer-perf-test --topic under-min-isr-promotion \ +docker-compose exec broker-west-1 kafka-producer-perf-test --topic multi-region-async-op-under-min-isr \ --num-records 5000 \ --record-size 5000 \ --throughput -1 \ @@ -49,7 +49,7 @@ docker-compose exec broker-west-1 kafka-producer-perf-test --topic under-min-isr compression.type=none \ batch.size=8196 -docker-compose exec broker-west-1 kafka-producer-perf-test --topic under-replicated-promotion \ +docker-compose exec broker-west-1 kafka-producer-perf-test --topic multi-region-async-op-under-replicated \ --num-records 5000 \ --record-size 5000 \ --throughput -1 \ @@ -59,7 +59,7 @@ docker-compose exec broker-west-1 kafka-producer-perf-test --topic under-replica compression.type=none \ batch.size=8196 -docker-compose exec broker-west-1 kafka-producer-perf-test --topic leader-is-observer-promotion \ +docker-compose exec broker-west-1 kafka-producer-perf-test --topic multi-region-async-op-leader-is-observer \ --num-records 5000 \ --record-size 5000 \ --throughput -1 \ From e40005a5fca4851948276ec19f3a099989c23e20 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 19 Nov 2020 19:08:18 +0000 Subject: [PATCH 20/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 8814ae26a..0aedd3903 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -554,7 +554,7 @@ Degraded Region In this section, you will simulate a single broker failure in the ``west`` region. -#. Run the following command to stop on of the broker Docker containers corresponding in the ``west`` region: +#. Run the following command to stop one of the broker Docker containers in the ``west`` region: .. code-block:: bash From f18bb797d2643339b1756a2010e0ba998c01e0b1 Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Thu, 19 Nov 2020 19:08:25 +0000 Subject: [PATCH 21/34] Update multiregion/docs/multiregion.rst Co-authored-by: Yeva Byzek --- multiregion/docs/multiregion.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 0aedd3903..b29c82b2e 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -41,7 +41,7 @@ An ``Observer`` is a broker/replica that also has a copy of data for a given topic-partition, and consumers are allowed to read from them even though the *Observer* isn't the leader–this is known as “Follower Fetching”. However, the data is copied asynchronously from the leader such that a producer doesn't wait -on observers to get back an acknowledgement. In non degraded states, observers don't +on observers to get back an acknowledgement. In "non-degraded" steady state, observers don't participate in the ISR list and won't become the leader. If a broker in the ISR fails, they could be promoted to the ISR list automatically with ``Observer Promotion`` or by manual changes to leader assignment. From aabed677f220ca5cf5678edda5903755dfbc8d2e Mon Sep 17 00:00:00 2001 From: thomaskwscott Date: Tue, 24 Nov 2020 11:23:44 +0000 Subject: [PATCH 22/34] KC-1029 updated replicas image to include observer promotion images --- .../images/multi-region-topic-replicas-v2.png | Bin 32526 -> 144738 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/multiregion/docs/images/multi-region-topic-replicas-v2.png b/multiregion/docs/images/multi-region-topic-replicas-v2.png index 781848c4518482dfada63d1d2d81ceaf79a12aa9..70dc27d918cd8a61bf33112fa40f97eb6b9ca412 100644 GIT binary patch literal 144738 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≶+6w3gZi{3S{}{}Q7vU-ZF(OB3^rQq6 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 literal 32526 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 O@={*yd9j??+y4V?lZF-m From 8b776e15cfdc3789de49012ff98d4f4407f0877c Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 11:28:34 -0500 Subject: [PATCH 23/34] Pull out build_docker_images.sh; tweak jmx metrics; docs improvements --- multiregion/docs/multiregion.rst | 57 ++++++++++++---------- multiregion/scripts/build_docker_images.sh | 11 +++++ multiregion/scripts/jmx_metrics.sh | 23 +++++++-- multiregion/scripts/start.sh | 14 +++--- 4 files changed, 69 insertions(+), 36 deletions(-) create mode 100755 multiregion/scripts/build_docker_images.sh diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b29c82b2e..090c86e90 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -41,21 +41,22 @@ An ``Observer`` is a broker/replica that also has a copy of data for a given topic-partition, and consumers are allowed to read from them even though the *Observer* isn't the leader–this is known as “Follower Fetching”. However, the data is copied asynchronously from the leader such that a producer doesn't wait -on observers to get back an acknowledgement. In "non-degraded" steady state, observers don't -participate in the ISR list and won't become the leader. If a broker in the ISR -fails, they could be promoted to the ISR list automatically with -``Observer Promotion`` or by manual changes to leader assignment. +on observers to get back an acknowledgement. |Follower_Fetching| -``Observer Promotion`` is the process whereby an observer is promoted into the -ISR in certain degraded scenarios. This behaviour is controlled by the -``observerPromotionPolicy`` field in a topic's replica placement policy. It can -have values: +In "non-degraded" steady state, observers don't participate in the ISR list and +won't become the leader. If a broker in the ISR fails, observers could be +promoted to the ISR list in one of two ways: manual changes to leader assignment, +or automatically with ``Observer Promotion``. +``Observer Promotion`` is the process by which an observer is promoted into the +ISR in certain "degraded" situations. The qualifications for whether an observer +can be automatically promoted into the ISR is controlled by the +``observerPromotionPolicy`` field in a topic's replica placement policy: -- under-min-isr: observers will be promoted if the isr size drops below the topic's min.insync.replicas configuration. -- under-replicated: observers will be promoted if the isr size drops below the configured count of replicas in the topic's replica placement policy. -- leader-is-observer: observers will only be promoted if the current partition leader is an observer. +- ``under-min-isr``: if the number of replicas in the ISR drops below the topic's ``min.insync.replicas`` configuration. +- ``under-replicated``: if the number of replicas in the ISR ISR drops below the configured count of replicas in the topic's replica placement policy. +- ``leader-is-observer``: if the current partition leader is an observer. Configuration @@ -68,7 +69,7 @@ The scenario for this tutorial is as follows: |Multi-region Architecture| -Here are some relevant configuration parameters: +Here are some relevant configuration parameters at different component levels: Broker ~~~~~~ @@ -136,7 +137,14 @@ Download and run the tutorial Startup ------- -#. Run the following command: +#. This |mrrep| example uses Traffic Control (``tc``) to inject latency between the regions and packet loss to simulate the +WAN link. Confluent's ubi-based Docker images do not have ``tc`` installed, so build custom Docker images with ``tc``. + + .. code-block:: bash + + ./scripts/build_docker_images.sh + +#. Start all the Docker containers .. code-block:: bash @@ -160,8 +168,7 @@ Startup Inject latency and packet loss ------------------------------ -This example uses Traffic Control (``tc``) to inject latency between the regions and packet loss to simulate the -WAN link. +Here is a diagram of the simulated latency between the regions and the WAN link. |Multi-region latencies| @@ -171,8 +178,8 @@ WAN link. docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq) -#. Run the script :devx-examples:`latency_docker.sh|multiregion/scripts/latency_docker.sh` that installs and configures - ``tc`` on the Docker containers to simulate the latency and packet loss: +#. Run the script :devx-examples:`latency_docker.sh|multiregion/scripts/latency_docker.sh` that configures + ``tc`` on the Docker containers: .. code-block:: bash @@ -506,47 +513,47 @@ There is a script you can run to collect the JMX metrics from the command line, .. code-block:: text - ==> Monitor ReplicasCount + ==> JMX metric: ReplicasCount single-region: 2 multi-region-sync: 4 multi-region-async: 4 - multi-region-default: 4 multi-region-async-op-under-min-isr: 4 multi-region-async-op-under-replicated: 4 multi-region-async-op-leader-is-observer: 4 + multi-region-default: 4 - ==> Monitor InSyncReplicasCount + ==> JMX metric: InSyncReplicasCount single-region: 2 multi-region-sync: 4 multi-region-async: 2 - multi-region-default: 2 multi-region-async-op-under-min-isr: 2 multi-region-async-op-under-replicated: 2 multi-region-async-op-leader-is-observer: 2 + multi-region-default: 2 - ==> Monitor CaughtUpReplicasCount + ==> JMX metric: CaughtUpReplicasCount single-region: 2 multi-region-sync: 4 multi-region-async: 4 - multi-region-default: 4 multi-region-async-op-under-min-isr: 4 multi-region-async-op-under-replicated: 4 multi-region-async-op-leader-is-observer: 4 + multi-region-default: 4 - ==> Monitor ObserversInIsrCount + ==> JMX metric: ObserversInIsrCount single-region: 0 multi-region-sync: 0 multi-region-async: 0 - multi-region-default: 0 multi-region-async-op-under-min-isr: 0 multi-region-async-op-under-replicated: 0 multi-region-async-op-leader-is-observer: 0 + multi-region-default: 0 Degraded Region diff --git a/multiregion/scripts/build_docker_images.sh b/multiregion/scripts/build_docker_images.sh new file mode 100755 index 000000000..908394d0e --- /dev/null +++ b/multiregion/scripts/build_docker_images.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +source ${DIR}/../.env + +# Confluent's ubi-based Docker images do not have 'tc' installed +echo +echo "Build custom cp-zookeeper and cp-server images with 'tc' installed" +for image in cp-zookeeper cp-server; do + docker build --build-arg CP_VERSION=${CONFLUENT_DOCKER_TAG} --build-arg REPOSITORY=${REPOSITORY} --build-arg IMAGE=$image -t localbuild/${image}-tc:${CONFLUENT_DOCKER_TAG} -f Dockerfile . +done diff --git a/multiregion/scripts/jmx_metrics.sh b/multiregion/scripts/jmx_metrics.sh index 93eda65ac..344c70c2c 100755 --- a/multiregion/scripts/jmx_metrics.sh +++ b/multiregion/scripts/jmx_metrics.sh @@ -3,14 +3,27 @@ for metric in ReplicasCount InSyncReplicasCount CaughtUpReplicasCount ObserversInIsrCount do - echo -e "\n\n==> Monitor $metric \n" + echo -e "\n\n==> JMX metric: $metric \n" for topic in single-region multi-region-sync multi-region-async multi-region-default multi-region-async-op-under-min-isr multi-region-async-op-under-replicated multi-region-async-op-leader-is-observer do - BW1=$(docker-compose exec broker-west-1 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8091/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) - BW2=$(docker-compose exec broker-west-2 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8092/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) - BE3=$(docker-compose exec broker-east-3 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8093/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) - BE4=$(docker-compose exec broker-east-4 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8094/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) + + eval docker-compose ps -q broker-west-1 > /dev/null \ + && BW1=$(docker-compose exec broker-west-1 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8091/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ + || BW1=0 + + eval docker-compose ps -q broker-west-2 > /dev/null \ + && BW2=$(docker-compose exec broker-west-2 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8092/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ + || BW2=0 + + eval docker-compose ps -q broker-east-3 > /dev/null \ + && BE3=$(docker-compose exec broker-east-3 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8093/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ + || BE3=0 + + eval docker-compose ps -q broker-east-4 > /dev/null \ + && BE4=$(docker-compose exec broker-east-4 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8094/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ + || BE4=0 + REPLICAS=$((BW1 + BW2 + BE3 + BE4)) echo "$topic: $REPLICAS" done diff --git a/multiregion/scripts/start.sh b/multiregion/scripts/start.sh index ec6bd5113..b92222903 100755 --- a/multiregion/scripts/start.sh +++ b/multiregion/scripts/start.sh @@ -5,13 +5,9 @@ source ${DIR}/../.env ${DIR}/stop.sh -# Confluent's ubi-based Docker images do not have 'tc' installed -echo -echo "Build custom cp-zookeeper and cp-server images with 'tc' installed" -for image in cp-zookeeper cp-server; do - docker build --build-arg CP_VERSION=${CONFLUENT_DOCKER_TAG} --build-arg REPOSITORY=${REPOSITORY} --build-arg IMAGE=$image -t localbuild/${image}-tc:${CONFLUENT_DOCKER_TAG} -f Dockerfile . -done +${DIR}/build_docker_images.sh +echo "Bring up docker-compose" docker-compose up -d echo "Sleeping 20 seconds" @@ -64,6 +60,8 @@ ${DIR}/describe-topics.sh echo "Sleeping 30 seconds" sleep 30 +${DIR}/jmx_metrics.sh + echo -e "\nFail west region" docker-compose stop broker-west-2 zookeeper-west @@ -86,6 +84,8 @@ ${DIR}/describe-topics.sh echo "Sleeping 5 seconds" sleep 5 +${DIR}/jmx_metrics.sh + ${DIR}/permanent-fallback.sh echo "Sleeping 30 seconds" @@ -100,3 +100,5 @@ echo "Sleeping 300 seconds until the leadership election restores the preferred sleep 300 ${DIR}/describe-topics.sh + +${DIR}/jmx_metrics.sh From b8c1f5bd7751cf8137b9c5b6271b8b8ba44c48f4 Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 13:29:05 -0500 Subject: [PATCH 24/34] Remove threads from kafka-consumer-perf-test due to KAFKA-10126 --- multiregion/scripts/run-consumer.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/multiregion/scripts/run-consumer.sh b/multiregion/scripts/run-consumer.sh index 9ad62348c..fc91e5f43 100755 --- a/multiregion/scripts/run-consumer.sh +++ b/multiregion/scripts/run-consumer.sh @@ -4,7 +4,6 @@ echo -e "\n\n==> Consume from east: Multi-region Async Replication reading from docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async \ --messages 5000 \ - --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 @@ -12,28 +11,24 @@ echo -e "\n\n==> Consume from east: Multi-region Async Replication reading from docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async \ --messages 5000 \ - --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-under-min-isr \ --messages 5000 \ - --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-under-replicated \ --messages 5000 \ - --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-leader-is-observer \ --messages 5000 \ - --threads 1 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ - --consumer.config /etc/kafka/demo/consumer-east.config \ No newline at end of file + --consumer.config /etc/kafka/demo/consumer-east.config From 92302615823024a1690dcc2f9c12e0af429d826d Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 13:39:46 -0500 Subject: [PATCH 25/34] Add colon in describe topic output --- multiregion/docs/multiregion.rst | 60 +++++++++++++------------- multiregion/scripts/describe-topics.sh | 16 +++---- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 090c86e90..3d360879a 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -343,37 +343,37 @@ You could create all the topics by running the script :devx-examples:`create-top .. code-block:: text - ==> Describe topic single-region + ==> Describe topic: single-region Topic: single-region PartitionCount: 1 ReplicationFactor: 2 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[]} Topic: single-region Partition: 0 Leader: 2 Replicas: 2,1 Isr: 2,1 Offline: - ==> Describe topic multi-region-sync + ==> Describe topic: multi-region-sync Topic: multi-region-sync PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}},{"count":2,"constraints":{"rack":"east"}}],"observers":[]} Topic: multi-region-sync Partition: 0 Leader: 1 Replicas: 1,2,3,4 Isr: 1,2,3,4 Offline: - ==> Describe topic multi-region-async + ==> Describe topic: multi-region-async Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic multi-region-default + ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic multi-region-async-op-under-min-isr + ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic multi-region-async-op-under-replicated + ==> Describe topic: multi-region-async-op-under-replicated Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic multi-region-async-op-leader-is-observer + ==> Describe topic: multi-region-async-op-leader-is-observer Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 @@ -577,37 +577,37 @@ In this section, you will simulate a single broker failure in the ``west`` regio .. code-block:: text - ==> Describe topic single-region + ==> Describe topic: single-region Topic: single-region PartitionCount: 1 ReplicationFactor: 2 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[]} Topic: single-region Partition: 0 Leader: 2 Replicas: 1,2 Isr: 2 Offline: 1 - ==> Describe topic multi-region-sync + ==> Describe topic: multi-region-sync Topic: multi-region-sync PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}},{"count":2,"constraints":{"rack":"east"}}],"observers":[]} Topic: multi-region-sync Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2,3,4 Offline: 1 - ==> Describe topic multi-region-async + ==> Describe topic: multi-region-async Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 1,2,4,3 Isr: 2 Offline: 1 Observers: 4,3 - ==> Describe topic multi-region-default + ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2 Offline: 1 Observers: 3,4 - ==> Describe topic multi-region-async-op-under-min-isr + ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 - ==> Describe topic multi-region-async-op-under-replicated + ==> Describe topic: multi-region-async-op-under-replicated Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,4 Offline: 1 Observers: 3,4 - ==> Describe topic multi-region-async-op-leader-is-observer + ==> Describe topic: multi-region-async-op-leader-is-observer Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2 Offline: 1 Observers: 3,4 @@ -652,37 +652,37 @@ In this section, you will simulate a region failure by bringing down the ``west` .. code-block:: text - ==> Describe topic single-region + ==> Describe topic: single-region Topic: single-region PartitionCount: 1 ReplicationFactor: 2 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[]} Topic: single-region Partition: 0 Leader: none Replicas: 2,1 Isr: 1 Offline: 2,1 - ==> Describe topic multi-region-sync + ==> Describe topic: multi-region-sync Topic: multi-region-sync PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}},{"count":2,"constraints":{"rack":"east"}}],"observers":[]} Topic: multi-region-sync Partition: 0 Leader: 3 Replicas: 1,2,3,4 Isr: 3,4 Offline: 1,2 - ==> Describe topic multi-region-async + ==> Describe topic: multi-region-async Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 - ==> Describe topic multi-region-default + ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 - ==> Describe topic multi-region-async-op-under-min-isr + ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 4 Replicas: 2,1,4,3 Isr: 4,3 Offline: 2,1 Observers: 4,3 - ==> Describe topic multi-region-async-op-under-replicated + ==> Describe topic: multi-region-async-op-under-replicated Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 4 Replicas: 1,2,3,4 Isr: 4,3 Offline: 1,2 Observers: 3,4 - ==> Describe topic multi-region-async-op-leader-is-observer + ==> Describe topic: multi-region-async-op-leader-is-observer Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: none Replicas: 1,2,4,3 Isr: 2 Offline: 1,2 Observers: 4,3 @@ -734,12 +734,12 @@ steps: .. code-block:: text ... - ==> Describe topic multi-region-async + ==> Describe topic: multi-region-async Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 2,1,3,4 Isr: 3,4 Offline: 2,1 Observers: 3,4 - ==> Describe topic multi-region-default + ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-default Partition: 0 Leader: 3 Replicas: 2,1,3,4 Isr: 3,4 Offline: 2,1 Observers: 3,4 @@ -790,7 +790,7 @@ the following steps: .. code-block:: text ... - ==> Describe topic multi-region-default + ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"east"}}],"observers":[{"count":2,"constraints":{"rack":"west"}}]} Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 3,4,2,1 Isr: 3,4 Offline: 2,1 Observers: 2,1 @@ -836,32 +836,32 @@ Now you will bring region ``west`` back online. Topic: single-region PartitionCount: 1 ReplicationFactor: 2 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[]} Topic: single-region Partition: 0 Leader: 2 Replicas: 2,1 Isr: 1,2 Offline: - ==> Describe topic multi-region-sync + ==> Describe topic: multi-region-sync Topic: multi-region-sync PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}},{"count":2,"constraints":{"rack":"east"}}],"observers":[]} Topic: multi-region-sync Partition: 0 Leader: 1 Replicas: 1,2,3,4 Isr: 3,4,2,1 Offline: - ==> Describe topic multi-region-async + ==> Describe topic: multi-region-async Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic multi-region-default + ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"east"}}],"observers":[{"count":2,"constraints":{"rack":"west"}}]} Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 3,4,2,1 Isr: 3,4 Offline: Observers: 2,1 - ==> Describe topic multi-region-async-op-under-min-isr + ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-min-isr Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 - ==> Describe topic multi-region-async-op-under-replicated + ==> Describe topic: multi-region-async-op-under-replicated Topic: multi-region-async-op-under-replicated PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"under-replicated","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-under-replicated Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 1,2 Offline: Observers: 3,4 - ==> Describe topic multi-region-async-op-leader-is-observer + ==> Describe topic: multi-region-async-op-leader-is-observer Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 diff --git a/multiregion/scripts/describe-topics.sh b/multiregion/scripts/describe-topics.sh index 1fe2bde54..7c3108fe5 100755 --- a/multiregion/scripts/describe-topics.sh +++ b/multiregion/scripts/describe-topics.sh @@ -1,36 +1,36 @@ #!/bin/bash -echo -e "\n==> Describe topic single-region\n" +echo -e "\n==> Describe topic: single-region\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic single-region -echo -e "\n==> Describe topic multi-region-sync\n" +echo -e "\n==> Describe topic: multi-region-sync\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-sync -echo -e "\n==> Describe topic multi-region-async\n" +echo -e "\n==> Describe topic: multi-region-async\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-async -echo -e "\n==> Describe topic multi-region-default\n" +echo -e "\n==> Describe topic: multi-region-default\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-default -echo -e "\n==> Describe topic multi-region-async-op-under-min-isr\n" +echo -e "\n==> Describe topic: multi-region-async-op-under-min-isr\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-under-min-isr -echo -e "\n==> Describe topic multi-region-async-op-under-replicated\n" +echo -e "\n==> Describe topic: multi-region-async-op-under-replicated\n" docker-compose exec broker-east-3 kafka-topics --describe \ --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-under-replicated -echo -e "\n==> Describe topic multi-region-async-op-leader-is-observer\n" +echo -e "\n==> Describe topic: multi-region-async-op-leader-is-observer\n" docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-leader-is-observer \ No newline at end of file + --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-leader-is-observer From b3b6757bd0113a5933b969cfa956b6bfcab269bb Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 13:59:45 -0500 Subject: [PATCH 26/34] Update JMX docker conatiner check; add output to start script --- multiregion/scripts/jmx_metrics.sh | 8 ++++---- multiregion/scripts/start.sh | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/multiregion/scripts/jmx_metrics.sh b/multiregion/scripts/jmx_metrics.sh index 344c70c2c..e7b414b95 100755 --- a/multiregion/scripts/jmx_metrics.sh +++ b/multiregion/scripts/jmx_metrics.sh @@ -8,19 +8,19 @@ do for topic in single-region multi-region-sync multi-region-async multi-region-default multi-region-async-op-under-min-isr multi-region-async-op-under-replicated multi-region-async-op-leader-is-observer do - eval docker-compose ps -q broker-west-1 > /dev/null \ + test "$(docker inspect -f '{{.State.ExitCode}}' $(docker ps -laq --filter="name=broker-west-1"))" = "0" \ && BW1=$(docker-compose exec broker-west-1 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8091/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ || BW1=0 - eval docker-compose ps -q broker-west-2 > /dev/null \ + test "$(docker inspect -f '{{.State.ExitCode}}' $(docker ps -laq --filter="name=broker-west-2"))" = "0" \ && BW2=$(docker-compose exec broker-west-2 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8092/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ || BW2=0 - eval docker-compose ps -q broker-east-3 > /dev/null \ + test "$(docker inspect -f '{{.State.ExitCode}}' $(docker ps -laq --filter="name=broker-east-3"))" = "0" \ && BE3=$(docker-compose exec broker-east-3 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8093/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ || BE3=0 - eval docker-compose ps -q broker-east-4 > /dev/null \ + test "$(docker inspect -f '{{.State.ExitCode}}' $(docker ps -laq --filter="name=broker-east-4"))" = "0" \ && BE4=$(docker-compose exec broker-east-4 kafka-run-class kafka.tools.JmxTool --jmx-url service:jmx:rmi:///jndi/rmi://localhost:8094/jmxrmi --object-name kafka.cluster:type=Partition,name=$metric,topic=$topic,partition=0 --one-time true | tail -n 1 | awk -F, '{print $2;}' | head -c 1) \ || BE4=0 diff --git a/multiregion/scripts/start.sh b/multiregion/scripts/start.sh index b92222903..1c7b1b0ac 100755 --- a/multiregion/scripts/start.sh +++ b/multiregion/scripts/start.sh @@ -32,6 +32,8 @@ ${DIR}/create-topics.sh echo -e "\nSleeping 5 seconds" sleep 5 +echo -e "\n=========== Steady state ===========" + ${DIR}/describe-topics.sh echo -e "\nSleeping 5 seconds" @@ -49,7 +51,7 @@ sleep 5 ${DIR}/jmx_metrics.sh -echo -e "\nDegrade west region" +echo -e "\n=========== Degrade west region ===========" docker-compose stop broker-west-1 echo "Sleeping 30 seconds" @@ -62,7 +64,7 @@ sleep 30 ${DIR}/jmx_metrics.sh -echo -e "\nFail west region" +echo -e "\n=========== Fail west region ===========" docker-compose stop broker-west-2 zookeeper-west echo "Sleeping 30 seconds" @@ -93,7 +95,7 @@ sleep 30 ${DIR}/describe-topics.sh -echo -e "\nRestore west region" +echo -e "\n=========== Restore west region ===========" docker-compose start broker-west-1 broker-west-2 zookeeper-west echo "Sleeping 300 seconds until the leadership election restores the preferred replicas" From fe0151bdaebb0a1a6b479b065f6097159be8a691 Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 14:11:38 -0500 Subject: [PATCH 27/34] Reorder topics in jmx_metrics --- multiregion/scripts/jmx_metrics.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiregion/scripts/jmx_metrics.sh b/multiregion/scripts/jmx_metrics.sh index e7b414b95..e95324670 100755 --- a/multiregion/scripts/jmx_metrics.sh +++ b/multiregion/scripts/jmx_metrics.sh @@ -5,7 +5,7 @@ do echo -e "\n\n==> JMX metric: $metric \n" - for topic in single-region multi-region-sync multi-region-async multi-region-default multi-region-async-op-under-min-isr multi-region-async-op-under-replicated multi-region-async-op-leader-is-observer + for topic in single-region multi-region-sync multi-region-async multi-region-async-op-under-min-isr multi-region-async-op-under-replicated multi-region-async-op-leader-is-observer multi-region-default do test "$(docker inspect -f '{{.State.ExitCode}}' $(docker ps -laq --filter="name=broker-west-1"))" = "0" \ From f01250106a7320b8b8fc3dd8e1e1cd7a6b9a8c73 Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 14:25:41 -0500 Subject: [PATCH 28/34] Add more JMX metric output to docs --- multiregion/docs/multiregion.rst | 121 ++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 4 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 3d360879a..b1178cb10 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -477,7 +477,7 @@ Consumer Monitoring ----------- +~~~~~~~~~~ In |cs| there are a few JMX metrics you should monitor for determining the health and state of a topic partition. The tutorial describes the following JMX @@ -502,8 +502,8 @@ There is a script you can run to collect the JMX metrics from the command line, #. Run the script :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the - JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``ObserversInIsrCount`` and - ``CaughtUpReplicasCount`` from each of the brokers: + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``CaughtUpReplicasCount``, and ``ObserversInIsrCount`` + from each of the brokers: .. code-block:: bash @@ -628,6 +628,63 @@ In this section, you will simulate a single broker failure in the ``west`` regio online replicas (1) is less than the intended number of non observer replicas from the replica placement (2). An observer is promoted to fulfill this requirement. +#. Run the script + :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``CaughtUpReplicasCount``, and ``ObserversInIsrCount`` + from each of the brokers: + + .. code-block:: bash + + ./scripts/jmx_metrics.sh + +#. Verify you see output similar to the following: + + .. code-block:: text + + ==> JMX metric: ReplicasCount + + single-region: 2 + multi-region-sync: 4 + multi-region-async: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 4 + multi-region-default: 4 + + + ==> JMX metric: InSyncReplicasCount + + single-region: 1 + multi-region-sync: 3 + multi-region-async: 1 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 1 + multi-region-default: 1 + + + ==> JMX metric: CaughtUpReplicasCount + + single-region: 1 + multi-region-sync: 4 + multi-region-async: 3 + multi-region-async-op-under-min-isr: 3 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 4 + multi-region-default: 3 + + + ==> JMX metric: ObserversInIsrCount + + single-region: 0 + multi-region-sync: 0 + multi-region-async: 0 + multi-region-async-op-under-min-isr: 1 + multi-region-async-op-under-replicated: 1 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 0 + + Failover and Failback --------------------- @@ -707,6 +764,62 @@ In this section, you will simulate a region failure by bringing down the ``west` promoted observers into the ISR and an observer has become the leader. This is because their replica placement policy has set ``observerPromotionPolicy`` to allow this. +#. Run the script + :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``CaughtUpReplicasCount``, and ``ObserversInIsrCount`` + from each of the brokers: + + .. code-block:: bash + + ./scripts/jmx_metrics.sh + +#. Verify you see output similar to the following: + + .. code-block:: text + + ==> JMX metric: ReplicasCount + + single-region: 0 + multi-region-sync: 4 + multi-region-async: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 4 + + + ==> JMX metric: InSyncReplicasCount + + single-region: 0 + multi-region-sync: 2 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + + + ==> JMX metric: CaughtUpReplicasCount + + single-region: 0 + multi-region-sync: 2 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + + + ==> JMX metric: ObserversInIsrCount + + single-region: 0 + multi-region-sync: 0 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + Failover Observers ~~~~~~~~~~~~~~~~~~ @@ -808,7 +921,7 @@ the following steps: Failback ~~~~~~~~ -Now you will bring region ``west`` back online. +Now you will bring region ``west`` back online and restore configuration to the same as in steady state. #. Run the following command to bring the ``west`` region back online: From a9ea6b9661febc24d28d8a1cec1ceed9a74a616e Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 14:26:56 -0500 Subject: [PATCH 29/34] Delete unnecessary consumes from run-consumer.sh --- multiregion/scripts/run-consumer.sh | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/multiregion/scripts/run-consumer.sh b/multiregion/scripts/run-consumer.sh index fc91e5f43..951a65a30 100755 --- a/multiregion/scripts/run-consumer.sh +++ b/multiregion/scripts/run-consumer.sh @@ -14,21 +14,3 @@ docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region- --broker-list broker-west-1:19091,broker-east-3:19093 \ --timeout 20000 \ --consumer.config /etc/kafka/demo/consumer-east.config - -docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-under-min-isr \ - --messages 5000 \ - --broker-list broker-west-1:19091,broker-east-3:19093 \ - --timeout 20000 \ - --consumer.config /etc/kafka/demo/consumer-east.config - -docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-under-replicated \ - --messages 5000 \ - --broker-list broker-west-1:19091,broker-east-3:19093 \ - --timeout 20000 \ - --consumer.config /etc/kafka/demo/consumer-east.config - -docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async-op-leader-is-observer \ - --messages 5000 \ - --broker-list broker-west-1:19091,broker-east-3:19093 \ - --timeout 20000 \ - --consumer.config /etc/kafka/demo/consumer-east.config From fd0a8964ff64a28406d96b2080a618b25e8178b2 Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 14:27:51 -0500 Subject: [PATCH 30/34] Add more JMX metrics to script run --- multiregion/scripts/start.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/multiregion/scripts/start.sh b/multiregion/scripts/start.sh index 1c7b1b0ac..71c598986 100755 --- a/multiregion/scripts/start.sh +++ b/multiregion/scripts/start.sh @@ -72,6 +72,8 @@ sleep 30 ${DIR}/describe-topics.sh +${DIR}/jmx_metrics.sh + echo -e "\nFail over the observers in the topic multi-region-async to the east region, trigger leader election" docker-compose exec broker-east-4 kafka-leader-election --bootstrap-server broker-east-4:19094 --election-type UNCLEAN --topic multi-region-async --partition 0 @@ -95,6 +97,8 @@ sleep 30 ${DIR}/describe-topics.sh +${DIR}/jmx_metrics.sh + echo -e "\n=========== Restore west region ===========" docker-compose start broker-west-1 broker-west-2 zookeeper-west From cb08b4682480020427f5a3d71293d156f94f4307 Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 15:25:35 -0500 Subject: [PATCH 31/34] Increase timeout in run-consumer.sh --- multiregion/scripts/run-consumer.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiregion/scripts/run-consumer.sh b/multiregion/scripts/run-consumer.sh index 951a65a30..4386cf210 100755 --- a/multiregion/scripts/run-consumer.sh +++ b/multiregion/scripts/run-consumer.sh @@ -5,12 +5,12 @@ echo -e "\n\n==> Consume from east: Multi-region Async Replication reading from docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async \ --messages 5000 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ - --timeout 20000 + --timeout 30000 echo -e "\n\n==> Consume from east: Multi-region Async Replication reading from Observer in east (topic: multi-region-async) \n" docker-compose exec broker-east-3 kafka-consumer-perf-test --topic multi-region-async \ --messages 5000 \ --broker-list broker-west-1:19091,broker-east-3:19093 \ - --timeout 20000 \ + --timeout 30000 \ --consumer.config /etc/kafka/demo/consumer-east.config From 45cbc7ea4a5bfa1eaeaad0ccfb1d145bd4dbfc3a Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 15:53:14 -0500 Subject: [PATCH 32/34] Update docs with more JMX metrics --- multiregion/docs/multiregion.rst | 261 ++++++++++++++++++---- multiregion/scripts/describe-topics.sh | 36 +-- multiregion/scripts/permanent-fallback.sh | 21 -- multiregion/scripts/start.sh | 12 +- 4 files changed, 229 insertions(+), 101 deletions(-) delete mode 100755 multiregion/scripts/permanent-fallback.sh diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index b1178cb10..4e5059160 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -241,14 +241,6 @@ You could create all the topics by running the script :devx-examples:`create-top - no - none - * - multi-region-default - - 1x west - - 1x west - - 2x east - - {1,2} - - yes - - none - * - multi-region-async-op-under-min-isr - 1x west - 1x west @@ -273,6 +265,15 @@ You could create all the topics by running the script :devx-examples:`create-top - no - leader-is-observer + * - multi-region-default + - 1x west + - 1x west + - 2x east + - {1,2} + - yes + - none + + #. Create the |ak| topic ``single-region``. .. literalinclude:: ../scripts/create-topics.sh @@ -300,11 +301,6 @@ You could create all the topics by running the script :devx-examples:`create-top .. literalinclude:: ../config/placement-multi-region-async.json -#. Create the |ak| topic ``multi-region-default``. Note that the ``--replica-placement`` argument is not used in order to demonstrate the default placement constraints. - - .. literalinclude:: ../scripts/create-topics.sh - :lines: 34-38 - #. Create the |ak| topic ``multi-region-async-op-under-min-isr``. .. literalinclude:: ../scripts/create-topics.sh @@ -332,6 +328,10 @@ You could create all the topics by running the script :devx-examples:`create-top .. literalinclude:: ../config/placement-multi-region-async-op-leader-is-observer.json +#. Create the |ak| topic ``multi-region-default``. Note that the ``--replica-placement`` argument is not used in order to demonstrate the default placement constraints. + + .. literalinclude:: ../scripts/create-topics.sh + :lines: 34-38 #. View the topic replica placement by running the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`: @@ -358,11 +358,6 @@ You could create all the topics by running the script :devx-examples:`create-top Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic: multi-region-default - - Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} @@ -378,6 +373,12 @@ You could create all the topics by running the script :devx-examples:`create-top Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + ==> Describe topic: multi-region-default + + Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + + #. Observe the following: - The ``multi-region-async``, ``multi-region-async-op-under-min-isr``, ``multi-region-async-op-under-replicated``, ``multi-region-async-op-leader-is-observer`` and ``multi-region-default`` topics have replicas @@ -592,11 +593,6 @@ In this section, you will simulate a single broker failure in the ``west`` regio Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 1,2,4,3 Isr: 2 Offline: 1 Observers: 4,3 - ==> Describe topic: multi-region-default - - Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2 Offline: 1 Observers: 3,4 - ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} @@ -612,6 +608,12 @@ In this section, you will simulate a single broker failure in the ``west`` regio Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2 Offline: 1 Observers: 3,4 + ==> Describe topic: multi-region-default + + Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-default Partition: 0 Leader: 2 Replicas: 1,2,3,4 Isr: 2 Offline: 1 Observers: 3,4 + + #. Observe the following: - In all topics except ``multi-region-async-op-under-min-isr``, ``multi-region-sync`` and ``multi-region-async-op-under-replicated`` @@ -724,11 +726,6 @@ In this section, you will simulate a region failure by bringing down the ``west` Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 - ==> Describe topic: multi-region-default - - Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} - Topic: multi-region-default Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 - ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} @@ -744,6 +741,12 @@ In this section, you will simulate a region failure by bringing down the ``west` Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: none Replicas: 1,2,4,3 Isr: 2 Offline: 1,2 Observers: 4,3 + ==> Describe topic: multi-region-default + + Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} + Topic: multi-region-default Partition: 0 Leader: none Replicas: 2,1,3,4 Isr: 1 Offline: 2,1 Observers: 3,4 + + #. Observe the following: - In the first scenario, the ``single-region`` topic has no leader, because @@ -781,44 +784,44 @@ In this section, you will simulate a region failure by bringing down the ``west` single-region: 0 multi-region-sync: 4 - multi-region-async: 4 + multi-region-async: 0 multi-region-async-op-under-min-isr: 4 multi-region-async-op-under-replicated: 4 multi-region-async-op-leader-is-observer: 0 - multi-region-default: 4 + multi-region-default: 0 ==> JMX metric: InSyncReplicasCount single-region: 0 multi-region-sync: 2 - multi-region-async: 2 + multi-region-async: 0 multi-region-async-op-under-min-isr: 2 multi-region-async-op-under-replicated: 2 multi-region-async-op-leader-is-observer: 0 - multi-region-default: 2 + multi-region-default: 0 ==> JMX metric: CaughtUpReplicasCount single-region: 0 multi-region-sync: 2 - multi-region-async: 2 + multi-region-async: 0 multi-region-async-op-under-min-isr: 2 multi-region-async-op-under-replicated: 2 multi-region-async-op-leader-is-observer: 0 - multi-region-default: 2 + multi-region-default: 0 ==> JMX metric: ObserversInIsrCount single-region: 0 multi-region-sync: 0 - multi-region-async: 2 + multi-region-async: 0 multi-region-async-op-under-min-isr: 2 multi-region-async-op-under-replicated: 2 multi-region-async-op-leader-is-observer: 0 - multi-region-default: 2 + multi-region-default: 0 Failover Observers @@ -852,6 +855,7 @@ steps: Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 2,1,3,4 Isr: 3,4 Offline: 2,1 Observers: 3,4 + ... ==> Describe topic: multi-region-default Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} @@ -864,6 +868,62 @@ steps: - The topics ``multi-region-async`` and ``multi-region-default`` had observers that are now in the ISR list (for example, replicas 3,4 in the previous output) +#. Run the script + :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``CaughtUpReplicasCount``, and ``ObserversInIsrCount`` + from each of the brokers: + + .. code-block:: bash + + ./scripts/jmx_metrics.sh + +#. Verify you see output similar to the following: + + .. code-block:: text + + ==> JMX metric: ReplicasCount + + single-region: 0 + multi-region-sync: 4 + multi-region-async: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 4 + + + ==> JMX metric: InSyncReplicasCount + + single-region: 0 + multi-region-sync: 2 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + + + ==> JMX metric: CaughtUpReplicasCount + + single-region: 0 + multi-region-sync: 2 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + + + ==> JMX metric: ObserversInIsrCount + + single-region: 0 + multi-region-sync: 0 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + Permanent Failover ~~~~~~~~~~~~~~~~~~ @@ -882,15 +942,15 @@ the following steps: #. Change the replica placement constraints configuration and replica assignment for ``multi-region-default``, by running the script - :devx-examples:`permanent-fallback.sh|multiregion/scripts/permanent-fallback.sh`. + :devx-examples:`permanent-failover.sh|multiregion/scripts/permanent-failover.sh`. .. code-block:: bash - ./scripts/permanent-fallback.sh + ./scripts/permanent-failover.sh The script uses ``kafka-configs`` to change the replica placement policy and then it runs ``confluent-rebalancer`` to move the replicas. - .. literalinclude:: ../scripts/permanent-fallback.sh + .. literalinclude:: ../scripts/permanent-failover.sh #. Describe the topics again with the script :devx-examples:`describe-topics.sh|multiregion/scripts/describe-topics.sh`. @@ -917,6 +977,62 @@ the following steps: - For topic ``multi-region-default``, replicas 3 and 4, which were previously observers, are now sync replicas. +#. Run the script + :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``CaughtUpReplicasCount``, and ``ObserversInIsrCount`` + from each of the brokers: + + .. code-block:: bash + + ./scripts/jmx_metrics.sh + +#. Verify you see output similar to the following: + + .. code-block:: text + + ==> JMX metric: ReplicasCount + + single-region: 0 + multi-region-sync: 4 + multi-region-async: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 4 + + + ==> JMX metric: InSyncReplicasCount + + single-region: 0 + multi-region-sync: 2 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + + + ==> JMX metric: CaughtUpReplicasCount + + single-region: 0 + multi-region-sync: 2 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 2 + + + ==> JMX metric: ObserversInIsrCount + + single-region: 0 + multi-region-sync: 0 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 0 + Failback ~~~~~~~~ @@ -959,11 +1075,6 @@ Now you will bring region ``west`` back online and restore configuration to the Topic: multi-region-async PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 - ==> Describe topic: multi-region-default - - Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"east"}}],"observers":[{"count":2,"constraints":{"rack":"west"}}]} - Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 3,4,2,1 Isr: 3,4 Offline: Observers: 2,1 - ==> Describe topic: multi-region-async-op-under-min-isr Topic: multi-region-async-op-under-min-isr PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=2,confluent.placement.constraints={"observerPromotionPolicy":"under-min-isr","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} @@ -979,6 +1090,12 @@ Now you will bring region ``west`` back online and restore configuration to the Topic: multi-region-async-op-leader-is-observer PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"observerPromotionPolicy":"leader-is-observer","version":2,"replicas":[{"count":2,"constraints":{"rack":"west"}}],"observers":[{"count":2,"constraints":{"rack":"east"}}]} Topic: multi-region-async-op-leader-is-observer Partition: 0 Leader: 2 Replicas: 2,1,3,4 Isr: 2,1 Offline: Observers: 3,4 + ==> Describe topic: multi-region-default + + Topic: multi-region-default PartitionCount: 1 ReplicationFactor: 4 Configs: min.insync.replicas=1,confluent.placement.constraints={"version":1,"replicas":[{"count":2,"constraints":{"rack":"east"}}],"observers":[{"count":2,"constraints":{"rack":"west"}}]} + Topic: multi-region-async Partition: 0 Leader: 3 Replicas: 3,4,2,1 Isr: 3,4 Offline: Observers: 2,1 + + #. Observe the following: - All topics have leaders again, in particular ``single-region`` which lost its @@ -1002,6 +1119,62 @@ Now you will bring region ``west`` back online and restore configuration to the observers will be lost because logs are truncated before catching up and joining the ISR. +#. Run the script + :devx-examples:`jmx_metrics.sh|multiregion/scripts/jmx_metrics.sh` to get the + JMX metrics for ``ReplicasCount``, ``InSyncReplicasCount``, ``CaughtUpReplicasCount``, and ``ObserversInIsrCount`` + from each of the brokers: + + .. code-block:: bash + + ./scripts/jmx_metrics.sh + +#. Verify you see output similar to the following, which should exactly match the output from the start of the tutorial at steady state: + + .. code-block:: text + + ==> JMX metric: ReplicasCount + + single-region: 2 + multi-region-sync: 4 + multi-region-async: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 4 + multi-region-default: 4 + + + ==> JMX metric: InSyncReplicasCount + + single-region: 2 + multi-region-sync: 4 + multi-region-async: 2 + multi-region-async-op-under-min-isr: 2 + multi-region-async-op-under-replicated: 2 + multi-region-async-op-leader-is-observer: 2 + multi-region-default: 2 + + + ==> JMX metric: CaughtUpReplicasCount + + single-region: 2 + multi-region-sync: 4 + multi-region-async: 4 + multi-region-async-op-under-min-isr: 4 + multi-region-async-op-under-replicated: 4 + multi-region-async-op-leader-is-observer: 4 + multi-region-default: 4 + + + ==> JMX metric: ObserversInIsrCount + + single-region: 0 + multi-region-sync: 0 + multi-region-async: 0 + multi-region-async-op-under-min-isr: 0 + multi-region-async-op-under-replicated: 0 + multi-region-async-op-leader-is-observer: 0 + multi-region-default: 0 + Stop the Tutorial ----------------- diff --git a/multiregion/scripts/describe-topics.sh b/multiregion/scripts/describe-topics.sh index 7c3108fe5..3d79473a4 100755 --- a/multiregion/scripts/describe-topics.sh +++ b/multiregion/scripts/describe-topics.sh @@ -1,36 +1,10 @@ #!/bin/bash -echo -e "\n==> Describe topic: single-region\n" +for topic in single-region multi-region-sync multi-region-async multi-region-async-op-under-min-isr multi-region-async-op-under-replicated multi-region-async-op-leader-is-observer multi-region-default +do -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic single-region + echo -e "\n==> Describe topic: $topic\n" -echo -e "\n==> Describe topic: multi-region-sync\n" + docker-compose exec broker-east-3 kafka-topics --describe --bootstrap-server broker-east-3:19093 --topic $topic -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-sync - -echo -e "\n==> Describe topic: multi-region-async\n" - -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-async - -echo -e "\n==> Describe topic: multi-region-default\n" - -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-default - -echo -e "\n==> Describe topic: multi-region-async-op-under-min-isr\n" - -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-under-min-isr - -echo -e "\n==> Describe topic: multi-region-async-op-under-replicated\n" - -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-under-replicated - -echo -e "\n==> Describe topic: multi-region-async-op-leader-is-observer\n" - -docker-compose exec broker-east-3 kafka-topics --describe \ - --bootstrap-server broker-east-3:19093 --topic multi-region-async-op-leader-is-observer +done diff --git a/multiregion/scripts/permanent-fallback.sh b/multiregion/scripts/permanent-fallback.sh deleted file mode 100755 index 8a95dab35..000000000 --- a/multiregion/scripts/permanent-fallback.sh +++ /dev/null @@ -1,21 +0,0 @@ -echo -e "\n==> Switching replica placement constraints for multi-region-default\n" - -docker-compose exec broker-east-3 kafka-configs \ - --bootstrap-server broker-east-3:19093 \ - --alter \ - --topic multi-region-default \ - --replica-placement /etc/kafka/demo/placement-multi-region-default-reverse.json - - -echo -e "\n==> Running Confluent Rebalancer on multi-region-default\n" - -docker-compose exec broker-east-3 confluent-rebalancer execute \ - --metrics-bootstrap-server broker-east-3:19093 \ - --bootstrap-server broker-east-3:19093 \ - --replica-placement-only \ - --topics multi-region-default \ - --force \ - --throttle 10000000 - -docker-compose exec broker-east-3 confluent-rebalancer finish \ - --bootstrap-server broker-east-3:19093 diff --git a/multiregion/scripts/start.sh b/multiregion/scripts/start.sh index 71c598986..708e837df 100755 --- a/multiregion/scripts/start.sh +++ b/multiregion/scripts/start.sh @@ -32,7 +32,7 @@ ${DIR}/create-topics.sh echo -e "\nSleeping 5 seconds" sleep 5 -echo -e "\n=========== Steady state ===========" +echo -e "\n=========== Steady state ===========\n" ${DIR}/describe-topics.sh @@ -51,7 +51,8 @@ sleep 5 ${DIR}/jmx_metrics.sh -echo -e "\n=========== Degrade west region ===========" +echo -e "\n=========== Degrade west region ===========\n" + docker-compose stop broker-west-1 echo "Sleeping 30 seconds" @@ -64,7 +65,8 @@ sleep 30 ${DIR}/jmx_metrics.sh -echo -e "\n=========== Fail west region ===========" +echo -e "\n=========== Fail west region ===========\n" + docker-compose stop broker-west-2 zookeeper-west echo "Sleeping 30 seconds" @@ -90,7 +92,7 @@ sleep 5 ${DIR}/jmx_metrics.sh -${DIR}/permanent-fallback.sh +${DIR}/permanent-failover.sh echo "Sleeping 30 seconds" sleep 30 @@ -99,7 +101,7 @@ ${DIR}/describe-topics.sh ${DIR}/jmx_metrics.sh -echo -e "\n=========== Restore west region ===========" +echo -e "\n=========== Restore west region ===========\n" docker-compose start broker-west-1 broker-west-2 zookeeper-west echo "Sleeping 300 seconds until the leadership election restores the preferred replicas" From 854d52b8ed87965c2584f5f4b759e47b0b5d571b Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 15:59:14 -0500 Subject: [PATCH 33/34] Add permanent-failover.sh --- multiregion/scripts/permanent-failover.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 multiregion/scripts/permanent-failover.sh diff --git a/multiregion/scripts/permanent-failover.sh b/multiregion/scripts/permanent-failover.sh new file mode 100755 index 000000000..8a95dab35 --- /dev/null +++ b/multiregion/scripts/permanent-failover.sh @@ -0,0 +1,21 @@ +echo -e "\n==> Switching replica placement constraints for multi-region-default\n" + +docker-compose exec broker-east-3 kafka-configs \ + --bootstrap-server broker-east-3:19093 \ + --alter \ + --topic multi-region-default \ + --replica-placement /etc/kafka/demo/placement-multi-region-default-reverse.json + + +echo -e "\n==> Running Confluent Rebalancer on multi-region-default\n" + +docker-compose exec broker-east-3 confluent-rebalancer execute \ + --metrics-bootstrap-server broker-east-3:19093 \ + --bootstrap-server broker-east-3:19093 \ + --replica-placement-only \ + --topics multi-region-default \ + --force \ + --throttle 10000000 + +docker-compose exec broker-east-3 confluent-rebalancer finish \ + --bootstrap-server broker-east-3:19093 From df954f8962ca5c4a4a29c7844f7a2253e3a119e1 Mon Sep 17 00:00:00 2001 From: Yeva Byzek Date: Tue, 24 Nov 2020 16:14:18 -0500 Subject: [PATCH 34/34] Promote section headings --- multiregion/docs/multiregion.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/multiregion/docs/multiregion.rst b/multiregion/docs/multiregion.rst index 4e5059160..1ce7757db 100644 --- a/multiregion/docs/multiregion.rst +++ b/multiregion/docs/multiregion.rst @@ -478,7 +478,7 @@ Consumer Monitoring -~~~~~~~~~~ +---------- In |cs| there are a few JMX metrics you should monitor for determining the health and state of a topic partition. The tutorial describes the following JMX @@ -687,8 +687,8 @@ In this section, you will simulate a single broker failure in the ``west`` regio multi-region-default: 0 -Failover and Failback ---------------------- +Failover +-------- Fail Region ~~~~~~~~~~~ @@ -1035,7 +1035,7 @@ the following steps: Failback -~~~~~~~~ +-------- Now you will bring region ``west`` back online and restore configuration to the same as in steady state.