Skip to content

Commit 259fca9

Browse files
committed
Add procedure that checks and fixed epoch table entries
1 parent 51f406a commit 259fca9

3 files changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Validate the epoch table and optionally fix mismatches.
4+
#
5+
# Usage:
6+
# scripts/check-and-fix-epoch-table.sh [epoch_no]
7+
#
8+
# epoch_no: specific epoch to check (default: -1 = all epochs)
9+
#
10+
# Requires PGPASSFILE to be set (or ~/.pgpass configured), and expects the
11+
# database name to be on the PGDATABASE env var, the first line of the pgpass
12+
# file, or passed explicitly via psql_args.
13+
#
14+
# Examples:
15+
# PGPASSFILE=config/pgpass-mainnet scripts/check-and-fix-epoch-table.sh
16+
# PGPASSFILE=config/pgpass-mainnet scripts/check-and-fix-epoch-table.sh 620
17+
set -euo pipefail
18+
19+
EPOCH_NO=${1:--1}
20+
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
21+
VALIDATE_SQL="$SCRIPT_DIR/validate-epoch-table.sql"
22+
FIX_SQL="$SCRIPT_DIR/fix-epoch-table.sql"
23+
24+
if [[ ! -f "$VALIDATE_SQL" || ! -f "$FIX_SQL" ]]; then
25+
echo "Error: expected $VALIDATE_SQL and $FIX_SQL next to this script." >&2
26+
exit 1
27+
fi
28+
29+
# Derive the DB name from PGDATABASE or pgpass (field 5 of first line).
30+
DB_NAME=${PGDATABASE:-}
31+
if [[ -z "$DB_NAME" && -n "${PGPASSFILE:-}" && -f "$PGPASSFILE" ]]; then
32+
DB_NAME=$(awk -F: 'NR==1{print $3}' "$PGPASSFILE")
33+
fi
34+
if [[ -z "$DB_NAME" ]]; then
35+
echo "Error: could not determine database name. Set PGDATABASE or PGPASSFILE." >&2
36+
exit 1
37+
fi
38+
39+
echo "Validating epoch table for database '$DB_NAME' (epoch_no=$EPOCH_NO)..."
40+
OUTPUT=$(psql -X --no-psqlrc -v ON_ERROR_STOP=1 -v epoch_no="$EPOCH_NO" -f "$VALIDATE_SQL" "$DB_NAME")
41+
42+
# Header has 2 lines (column names + separator). Tail has "(N rows)".
43+
# Count only data rows by filtering lines that look like data (start with a digit).
44+
MISMATCHES=$(echo "$OUTPUT" | awk '/^[[:space:]]*[0-9]/ {c++} END {print c+0}')
45+
46+
if [[ "$MISMATCHES" -eq 0 ]]; then
47+
echo "No mismatches. Epoch table is consistent."
48+
exit 0
49+
fi
50+
51+
echo
52+
echo "Found $MISMATCHES mismatching epoch(s):"
53+
echo "$OUTPUT"
54+
echo
55+
56+
read -r -p "Apply fix? [y/N] " REPLY
57+
case "$REPLY" in
58+
[yY]|[yY][eE][sS])
59+
echo "Applying fix..."
60+
psql -X --no-psqlrc -v ON_ERROR_STOP=1 -v epoch_no="$EPOCH_NO" -f "$FIX_SQL" "$DB_NAME"
61+
echo "Fix applied. Re-validating..."
62+
RECHECK=$(psql -X --no-psqlrc -v ON_ERROR_STOP=1 -v epoch_no="$EPOCH_NO" -f "$VALIDATE_SQL" "$DB_NAME")
63+
REMAINING=$(echo "$RECHECK" | awk '/^[[:space:]]*[0-9]/ {c++} END {print c+0}')
64+
if [[ "$REMAINING" -eq 0 ]]; then
65+
echo "Epoch table is now consistent."
66+
else
67+
echo "Warning: $REMAINING mismatch(es) remain:" >&2
68+
echo "$RECHECK" >&2
69+
exit 1
70+
fi
71+
;;
72+
*)
73+
echo "No changes applied."
74+
exit 0
75+
;;
76+
esac

scripts/fix-epoch-table.sql

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
-- Fix epoch table values by recalculating from block/tx tables and upserting.
2+
--
3+
-- Usage:
4+
-- Fix a specific epoch:
5+
-- psql -v epoch_no=620 -f scripts/fix-epoch-table.sql cexplorer
6+
--
7+
-- Fix all epochs:
8+
-- psql -v epoch_no=-1 -f scripts/fix-epoch-table.sql cexplorer
9+
--
10+
-- Wrap in a transaction so failures don't partially rewrite the table.
11+
12+
BEGIN;
13+
14+
WITH calculated AS (
15+
SELECT
16+
b.epoch_no,
17+
COUNT(DISTINCT b.id) AS blk_count,
18+
MIN(b.time) AS start_time,
19+
MAX(b.time) AS end_time,
20+
COALESCE(SUM(tx.out_sum), 0) AS out_sum,
21+
COALESCE(SUM(tx.fee), 0) AS fee_sum,
22+
COUNT(tx.id) AS tx_count
23+
FROM block b
24+
LEFT JOIN tx ON tx.block_id = b.id
25+
WHERE b.epoch_no IS NOT NULL
26+
AND (CAST(:epoch_no AS bigint) = -1 OR b.epoch_no = CAST(:epoch_no AS bigint))
27+
GROUP BY b.epoch_no
28+
)
29+
INSERT INTO epoch (no, out_sum, fees, tx_count, blk_count, start_time, end_time)
30+
SELECT
31+
epoch_no,
32+
out_sum,
33+
fee_sum,
34+
tx_count,
35+
blk_count,
36+
start_time,
37+
end_time
38+
FROM calculated
39+
ON CONFLICT (no) DO UPDATE SET
40+
out_sum = EXCLUDED.out_sum,
41+
fees = EXCLUDED.fees,
42+
tx_count = EXCLUDED.tx_count,
43+
blk_count = EXCLUDED.blk_count,
44+
start_time = EXCLUDED.start_time,
45+
end_time = EXCLUDED.end_time
46+
WHERE
47+
epoch.out_sum IS DISTINCT FROM EXCLUDED.out_sum
48+
OR epoch.fees IS DISTINCT FROM EXCLUDED.fees
49+
OR epoch.tx_count IS DISTINCT FROM EXCLUDED.tx_count
50+
OR epoch.blk_count IS DISTINCT FROM EXCLUDED.blk_count
51+
OR epoch.start_time IS DISTINCT FROM EXCLUDED.start_time
52+
OR epoch.end_time IS DISTINCT FROM EXCLUDED.end_time;
53+
54+
COMMIT;

scripts/validate-epoch-table.sql

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
-- Validate epoch table values against recalculated values from tx/block tables.
2+
--
3+
-- Usage:
4+
-- Validate a specific epoch:
5+
-- psql -v epoch_no=620 -f scripts/validate-epoch-table.sql cexplorer
6+
--
7+
-- Validate all epochs:
8+
-- psql -v epoch_no=-1 -f scripts/validate-epoch-table.sql cexplorer
9+
--
10+
-- Any row in the output means a mismatch. No rows = all correct.
11+
12+
WITH calculated AS (
13+
SELECT
14+
b.epoch_no,
15+
COUNT(DISTINCT b.id) AS blk_count,
16+
MIN(b.time) AS start_time,
17+
MAX(b.time) AS end_time,
18+
COALESCE(SUM(tx.out_sum), 0) AS out_sum,
19+
COALESCE(SUM(tx.fee), 0) AS fee_sum,
20+
COUNT(tx.id) AS tx_count
21+
FROM block b
22+
LEFT JOIN tx ON tx.block_id = b.id
23+
WHERE b.epoch_no IS NOT NULL
24+
AND (CAST(:epoch_no AS bigint) = -1 OR b.epoch_no = CAST(:epoch_no AS bigint))
25+
GROUP BY b.epoch_no
26+
),
27+
compared AS (
28+
SELECT
29+
c.epoch_no,
30+
-- Calculated values
31+
c.out_sum AS calc_out_sum,
32+
c.fee_sum AS calc_fees,
33+
c.tx_count AS calc_tx_count,
34+
c.blk_count AS calc_blk_count,
35+
c.start_time AS calc_start_time,
36+
c.end_time AS calc_end_time,
37+
-- Stored values
38+
e.out_sum AS stored_out_sum,
39+
e.fees AS stored_fees,
40+
e.tx_count AS stored_tx_count,
41+
e.blk_count AS stored_blk_count,
42+
e.start_time AS stored_start_time,
43+
e.end_time AS stored_end_time,
44+
-- Differences
45+
c.out_sum - e.out_sum AS out_sum_diff,
46+
c.fee_sum - e.fees AS fees_diff,
47+
c.tx_count - e.tx_count AS tx_count_diff,
48+
c.blk_count - e.blk_count AS blk_count_diff
49+
FROM calculated c
50+
LEFT JOIN epoch e ON e.no = c.epoch_no
51+
)
52+
SELECT
53+
epoch_no,
54+
CASE WHEN stored_out_sum IS NULL THEN 'MISSING' ELSE '' END AS status,
55+
calc_out_sum,
56+
stored_out_sum,
57+
out_sum_diff,
58+
calc_fees,
59+
stored_fees,
60+
fees_diff,
61+
calc_tx_count,
62+
stored_tx_count,
63+
tx_count_diff,
64+
calc_blk_count,
65+
stored_blk_count,
66+
blk_count_diff,
67+
calc_start_time,
68+
stored_start_time,
69+
calc_end_time,
70+
stored_end_time
71+
FROM compared
72+
WHERE stored_out_sum IS NULL
73+
OR out_sum_diff != 0
74+
OR fees_diff != 0
75+
OR tx_count_diff != 0
76+
OR blk_count_diff != 0
77+
ORDER BY epoch_no;

0 commit comments

Comments
 (0)