m7_smoke.sh 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #!/usr/bin/env bash
  2. # Live M7 smoke test. Run from repo root:
  3. # bash scripts/m7_smoke.sh
  4. #
  5. # Walks through the 4 scenarios in M7_VERIFICATION.md:
  6. #
  7. # Step 2 — hypertable: query timescaledb_information.hypertables,
  8. # expect `deliveries` listed.
  9. # Step 3 — retention policy: query
  10. # timescaledb_information.jobs, expect a 7-day
  11. # retention policy on `deliveries`.
  12. # Step 4 — archiver dry-run: insert a synthetic 8d-old
  13. # row, run archiverd once, expect the row to
  14. # move from Postgres to ClickHouse and the
  15. # Postgres count to drop by 1.
  16. # Step 5 — ClickHouse archive: SELECT count(*), min, max
  17. # from ba_archive.deliveries_archive; expect
  18. # ≥1 row with created_at in the 7-30d range.
  19. #
  20. # Exit code is the number of failed checks.
  21. set -e
  22. cd "$(dirname "$0")/.."
  23. PGCMD="docker exec -i broad-announce-postgres-1 psql -U ba -d ba -A -t"
  24. CHURL=http://localhost:8123
  25. CHCMD="curl -sS -X POST $CHURL/"
  26. fails=0
  27. pass() { echo " ✅ $*"; }
  28. fail() { echo " ❌ $*"; fails=$((fails+1)); }
  29. ch_query() {
  30. $CHCMD --data-binary "$1" 2>/dev/null
  31. }
  32. # ── Setup: build archiverd fresh (must be v4 or later) ──
  33. ARCHIVERD_BIN=/tmp/archiverd-m7
  34. if [[ ! -x $ARCHIVERD_BIN ]]; then
  35. echo "▸ Building $ARCHIVERD_BIN"
  36. CGO_ENABLED=0 go build -o $ARCHIVERD_BIN ./cmd/archiverd
  37. fi
  38. # Always copy the fresh build into the container. Earlier
  39. # dev runs may have left a stale binary at /tmp/archiverd.
  40. docker exec broad-announce-ingestd-1 rm -f /tmp/archiverd 2>/dev/null || true
  41. # ─────────────────────────────────────────────────────────────────
  42. echo "── M7 smoke — Timescale 7d hot + ClickHouse archive ──"
  43. echo ""
  44. # ── Step 2: hypertable exists ──────────────────────────────
  45. echo "── Step 2: deliveries is a Timescale hypertable ──"
  46. hypertables=$($PGCMD -c "SELECT hypertable_name FROM timescaledb_information.hypertables;" 2>/dev/null | head -5)
  47. if echo "$hypertables" | grep -q "deliveries"; then
  48. pass "deliveries is a hypertable"
  49. else
  50. fail "deliveries NOT in hypertables list: $hypertables"
  51. fi
  52. # ── Step 3: retention policy is 7d ─────────────────────────
  53. echo ""
  54. echo "── Step 3: 7-day retention policy is active ──"
  55. jobs=$($PGCMD -c "SELECT application_name, config FROM timescaledb_information.jobs WHERE config::text LIKE '%7 days%';" 2>/dev/null | head -5)
  56. if echo "$jobs" | grep -q "7 days"; then
  57. pass "7-day retention policy present ($jobs)"
  58. else
  59. fail "7-day retention policy not found in jobs: $jobs"
  60. fi
  61. # ── Step 4: archiver moves a synthetic 8d-old row ─────────
  62. echo ""
  63. echo "── Step 4: 8d-old synthetic row moves to ClickHouse ──"
  64. # Insert a unique row, run archiverd, verify it moved.
  65. TAG="m7-step4-$(date +%s%N)"
  66. $PGCMD -c "INSERT INTO deliveries (alert_id, company_id, individual_id, channel, target, status, attempts, last_error, payload, created_at, sent_at) VALUES ('$TAG', 'acme-001', 'ind-acme-001', 'fcm', 'fake-token', 'sent', 0, '', '{\"smoke_tag\":\"$TAG\"}'::jsonb, now() - INTERVAL '8 days', now() - INTERVAL '8 days') ON CONFLICT DO NOTHING;" >/dev/null 2>&1
  67. pg_before=$($PGCMD -c "SELECT count(*) FROM deliveries WHERE alert_id = '$TAG';" 2>/dev/null | head -1)
  68. ch_before=$(ch_query "SELECT count(*) FROM ba_archive.deliveries_archive WHERE alert_id = '$TAG' FORMAT TabSeparated" 2>/dev/null | head -1)
  69. if [[ "$pg_before" == "1" ]]; then
  70. pass "synthetic row in postgres (count=1)"
  71. else
  72. fail "synthetic row not in postgres: count=$pg_before"
  73. fi
  74. # Run archiverd once. We use --port 8807 to avoid clashing
  75. # with anything left running in dev.
  76. docker exec broad-announce-ingestd-1 pkill -9 -f archiverd 2>/dev/null || true
  77. sleep 1
  78. docker cp $ARCHIVERD_BIN broad-announce-ingestd-1:/tmp/archiverd 2>/dev/null
  79. timeout 10 docker exec \
  80. -e BA_ENV=dev -e BA_HTTP_ADDR=:8807 \
  81. -e BA_POSTGRES_DSN="postgres://ba:ba@postgres:5432/ba?sslmode=disable" \
  82. -e BA_ARCHIVERD_CLICKHOUSE_URL="http://clickhouse:8123" \
  83. -e BA_ARCHIVERD_RUN_EVERY_SECONDS=999999 \
  84. -e BA_ARCHIVERD_OLDER_THAN_HOURS=168 \
  85. -e BA_ARCHIVERD_BATCH_SIZE=10000 \
  86. -e BA_LOG_LEVEL=info \
  87. broad-announce-ingestd-1 /tmp/archiverd > /tmp/m7-archiverd.log 2>&1 || true
  88. if grep -q "archiver run ok" /tmp/m7-archiverd.log; then
  89. pass "archiverd run completed ok"
  90. else
  91. fail "archiverd did not complete; log tail:"
  92. tail -10 /tmp/m7-archiverd.log | sed 's/^/ /'
  93. fi
  94. pg_after=$($PGCMD -c "SELECT count(*) FROM deliveries WHERE alert_id = '$TAG';" 2>/dev/null | head -1)
  95. ch_after=$(ch_query "SELECT count(*) FROM ba_archive.deliveries_archive WHERE alert_id = '$TAG' FORMAT TabSeparated" 2>/dev/null | head -1)
  96. if [[ "$pg_after" == "0" ]]; then
  97. pass "row moved out of postgres (count=0 after)"
  98. else
  99. fail "row still in postgres: count=$pg_after"
  100. fi
  101. if [[ "$ch_after" == "1" ]]; then
  102. pass "row landed in ClickHouse (count=1)"
  103. else
  104. fail "row missing from ClickHouse: count=$ch_after (was $ch_before)"
  105. fi
  106. # ── Step 5: ClickHouse aggregate query ─────────────────────
  107. echo ""
  108. echo "── Step 5: ClickHouse archive queryable ──"
  109. archive_count=$(ch_query "SELECT count(*) FROM ba_archive.deliveries_archive FORMAT TabSeparated" 2>/dev/null | head -1)
  110. if [[ ${archive_count:-0} -ge 1 ]]; then
  111. pass "ba_archive.deliveries_archive count = $archive_count (≥1 row archived)"
  112. else
  113. fail "ClickHouse archive empty (count=$archive_count)"
  114. fi
  115. min_age=$(ch_query "SELECT dateDiff('day', min(created_at), now()) FROM ba_archive.deliveries_archive FORMAT TabSeparated" 2>/dev/null | head -1)
  116. if [[ ${min_age:-999} -ge 7 ]]; then
  117. pass "oldest archived row is $min_age days old (≥7)"
  118. else
  119. fail "oldest archived row is only $min_age days old (expected ≥7)"
  120. fi
  121. # ── Summary ─────────────────────────────────────────────────
  122. echo ""
  123. if [[ $fails -eq 0 ]]; then
  124. echo "🟢 M7 smoke PASS — all checks green"
  125. exit 0
  126. else
  127. echo "🔴 M7 smoke FAIL — $fails check(s) failed"
  128. exit $fails
  129. fi