127 lines
4.6 KiB
Bash
Executable File
127 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Local services start RocketMQ consumers before any business event is produced,
|
|
# so relying on producer-side auto topic creation makes a fresh broker fail at
|
|
# startup. Keep the fixed owner-outbox topics explicit and idempotent here.
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
cd "${PROJECT_ROOT}"
|
|
|
|
"${SCRIPT_DIR}/prepare-local-rocketmq-config.sh" >/dev/null
|
|
export ROCKETMQ_BROKER_CONFIG="${PROJECT_ROOT}/tmp/rocketmq/broker.local.conf"
|
|
"${SCRIPT_DIR}/resolve-compose-container-conflicts.sh" rocketmq-namesrv rocketmq-broker
|
|
running_services="$(docker compose ps --services --status running 2>/dev/null || true)"
|
|
namesrv_was_running=0
|
|
broker_was_running=0
|
|
if grep -qx "rocketmq-namesrv" <<<"${running_services}"; then
|
|
namesrv_was_running=1
|
|
else
|
|
docker compose up -d rocketmq-namesrv >/dev/null
|
|
fi
|
|
if grep -qx "rocketmq-broker" <<<"${running_services}"; then
|
|
broker_was_running=1
|
|
fi
|
|
|
|
# `make run` is invoked repeatedly during local development. Recreating the
|
|
# broker on every run forces RocketMQ through store recovery, broker
|
|
# registration, and route propagation even when the same container is already
|
|
# healthy. Keep the existing broker hot by default, and reserve a forced
|
|
# recreate for explicit config refreshes via ROCKETMQ_FORCE_RECREATE=1.
|
|
if [[ "${ROCKETMQ_FORCE_RECREATE:-0}" == "1" ]]; then
|
|
docker compose up -d --force-recreate rocketmq-broker >/dev/null
|
|
broker_was_running=0
|
|
elif [[ "${broker_was_running}" != "1" ]]; then
|
|
docker compose up -d rocketmq-broker >/dev/null
|
|
fi
|
|
|
|
MQADMIN="/home/rocketmq/rocketmq-5.3.1/bin/mqadmin"
|
|
NAMESRV="rocketmq-namesrv:9876"
|
|
CLUSTER="hyapp-local"
|
|
TOPIC_STAMP="${PROJECT_ROOT}/tmp/rocketmq/topics.applied"
|
|
TOPICS=(
|
|
"hyapp_wallet_outbox"
|
|
"hyapp_wallet_realtime_outbox"
|
|
"hyapp_room_outbox"
|
|
"hyapp_room_rocket_launch"
|
|
"hyapp_user_outbox"
|
|
"hyapp_message_action_outbox"
|
|
"hyapp_game_outbox"
|
|
)
|
|
|
|
topic_fingerprint() {
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
printf '%s\n' "${TOPICS[@]}" | shasum -a 256 | awk '{print $1}'
|
|
return
|
|
fi
|
|
printf '%s\n' "${TOPICS[@]}" | sha256sum | awk '{print $1}'
|
|
}
|
|
|
|
write_topic_stamp() {
|
|
local broker_container_id="$1"
|
|
local fingerprint="$2"
|
|
|
|
# The stamp is only a local startup accelerator. It is scoped to the Docker
|
|
# container id plus the exact topic list, so replacing the broker or adding a
|
|
# new owner-outbox topic automatically falls back to mqadmin verification.
|
|
{
|
|
printf 'broker_container_id=%s\n' "${broker_container_id}"
|
|
printf 'topics_sha256=%s\n' "${fingerprint}"
|
|
for topic in "${TOPICS[@]}"; do
|
|
printf 'topic=%s\n' "${topic}"
|
|
done
|
|
} >"${TOPIC_STAMP}"
|
|
}
|
|
|
|
broker_container_id="$(docker compose ps -q rocketmq-broker)"
|
|
topics_sha256="$(topic_fingerprint)"
|
|
if [[ "${namesrv_was_running}" == "1" && "${broker_was_running}" == "1" && -n "${broker_container_id}" && -f "${TOPIC_STAMP}" ]]; then
|
|
if grep -Fxq "broker_container_id=${broker_container_id}" "${TOPIC_STAMP}" && grep -Fxq "topics_sha256=${topics_sha256}" "${TOPIC_STAMP}"; then
|
|
printf 'rocketmq topics already applied\n'
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# The broker process can accept a container start before it has registered with
|
|
# NameServer. Wait for the cluster view so topic checks and updateTopic target
|
|
# the real broker instead of racing the route table.
|
|
cluster_view=""
|
|
for _ in {1..60}; do
|
|
if cluster_view="$(docker compose exec -T rocketmq-broker "${MQADMIN}" clusterList -n "${NAMESRV}" 2>/dev/null)" && grep -q "${CLUSTER}" <<<"${cluster_view}"; then
|
|
break
|
|
fi
|
|
|
|
sleep 1
|
|
done
|
|
|
|
if ! grep -q "${CLUSTER}" <<<"${cluster_view}"; then
|
|
printf 'rocketmq broker did not register in cluster %s before topic init\n' "${CLUSTER}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Topic creation is idempotent, but each updateTopic call shells into the
|
|
# broker container and blocks local startup. Query once and only create missing
|
|
# topics; a warm `make run` then pays one cheap route read instead of seven
|
|
# management writes.
|
|
topic_list="$(docker compose exec -T rocketmq-broker "${MQADMIN}" topicList -n "${NAMESRV}" 2>/dev/null || true)"
|
|
missing_topics=()
|
|
for topic in "${TOPICS[@]}"; do
|
|
if grep -Fxq "${topic}" <<<"${topic_list}"; then
|
|
continue
|
|
fi
|
|
missing_topics+=("${topic}")
|
|
done
|
|
|
|
if [[ ${#missing_topics[@]} -eq 0 ]]; then
|
|
write_topic_stamp "${broker_container_id}" "${topics_sha256}"
|
|
printf 'rocketmq topics already applied\n'
|
|
exit 0
|
|
fi
|
|
|
|
for topic in "${missing_topics[@]}"; do
|
|
printf 'applying rocketmq topic: %s\n' "${topic}"
|
|
docker compose exec -T rocketmq-broker "${MQADMIN}" updateTopic -n "${NAMESRV}" -c "${CLUSTER}" -t "${topic}" -r 8 -w 8 >/dev/null
|
|
done
|
|
write_topic_stamp "${broker_container_id}" "${topics_sha256}"
|