feat:
--ADDED testCase.txt --ADDED testParams.txt
This commit is contained in:
1 parent
89b4fb8ab9
commit
476ab54c6d
2 files changed
+1028
No files matched your search
+963
@@ -0,0 +1,963 @@
|
||||
Yes. At this point, the next phase should be **systematic failure testing**. A multi-master cluster is not production-ready just because `INSERT` on Node1 appears on Node2 and Node3.
|
||||
|
||||
One important thing first: your setup has been using **Spock 5.0.1**. pgEdge's current v5 release notes list **5.0.12**, with fixes around connection-loss recovery, exception handling, apply workers, memory growth and newer PostgreSQL security changes. Before production, I would include a controlled upgrade rehearsal to a current supported 5.x patch rather than certify 5.0.1 as-is. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/development/spock_release_notes/?utm_source=chatgpt.com)
|
||||
|
||||
Do the destructive tests below on a staging copy of the cluster, not on the only copy of important data.
|
||||
|
||||
## 1. Establish your normal baseline first
|
||||
|
||||
Before deliberately breaking anything, all three nodes should show healthy subscriptions and essentially zero lag.
|
||||
|
||||
Run on every node:
|
||||
|
||||
```sql
|
||||
SELECT * FROM spock.sub_show_status();
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT * FROM spock.lag_tracker;
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
slot_name,
|
||||
active,
|
||||
wal_status,
|
||||
restart_lsn,
|
||||
confirmed_flush_lsn
|
||||
FROM pg_replication_slots;
|
||||
```
|
||||
|
||||
Check:
|
||||
|
||||
```sql
|
||||
SHOW track_commit_timestamp;
|
||||
SHOW spock.conflict_resolution;
|
||||
SHOW spock.exception_behaviour;
|
||||
```
|
||||
|
||||
And:
|
||||
|
||||
```sql
|
||||
SELECT *
|
||||
FROM spock.exception_log
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 50;
|
||||
```
|
||||
|
||||
`track_commit_timestamp` needs to be enabled for timestamp-based Spock conflict resolution such as `last_update_wins`. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/development/configuring/?utm_source=chatgpt.com)
|
||||
|
||||
Your baseline should be:
|
||||
|
||||
```text
|
||||
Node1 → Node2 ✓
|
||||
Node1 → Node3 ✓
|
||||
|
||||
Node2 → Node1 ✓
|
||||
Node2 → Node3 ✓
|
||||
|
||||
Node3 → Node1 ✓
|
||||
Node3 → Node2 ✓
|
||||
|
||||
Lag → approximately 0
|
||||
Exceptions → none unexplained
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Production test matrix
|
||||
|
||||
| ID | Scenario | Test | Production PASS condition |
|
||||
| --- |--------------------------|-------------------------------------------------------|---------------------------|
|
||||
| P01 | Node1 INSERT | Insert unique rows on Node1 | Node2 + Node3 receive exact rows |(pass)
|
||||
| P02 | Node2 INSERT | Write only Node2 | Node1 + Node3 receive them |(pass)
|
||||
| P03 | Node3 INSERT | Write only Node3 | Node1 + Node2 receive them |(pass)
|
||||
| P04 | UPDATE/DELETE | Perform updates/deletes from every node | All 3 converge |(pass)
|
||||
| P05 | PostgreSQL restart | Restart one PostgreSQL container | Replication reconnects automatically | (pass)
|
||||
| P06 | VM reboot | Reboot one VM completely | DB + Spock recover and catch up |(pass)
|
||||
| P07 | Short outage | Node offline 5–30 min while others write | All backlog catches up |(inprogress)
|
||||
| P08 | Long backlog | Generate hours/days-equivalent WAL while node offline | Catch-up succeeds without WAL loss/disk exhaustion |
|
||||
| P09 | Network partition | Node stays writable but cannot replicate | Reconnect works and conflicts behave as designed |
|
||||
| P10 | Same-row UPDATE | Modify same PK concurrently on 2 nodes | Expected conflict policy wins |
|
||||
| P11 | Duplicate PK | Insert same PK independently on 2 nodes | Conflict detected/resolved as expected |
|
||||
| P12 | Unique constraint | Different PKs but same UNIQUE value | Failure/conflict is handled and alerted |
|
||||
| P13 | DELETE vs UPDATE | Delete a row on one node, update on another | Expected result + exception handling verified |
|
||||
| P14 | Business conflict | Two nodes decrement same inventory/balance | Business invariant remains correct or architecture changed |
|
||||
| P15 | DDL | CREATE/ALTER during maintenance procedure | Identical schema across all nodes |
|
||||
| P16 | Node-down DDL | One node unavailable during schema migration | Recovery procedure proven |
|
||||
| P17 | Schema mismatch | Deliberately introduce mismatch in staging | Monitoring detects it; recovery works |
|
||||
| P18 | Disk pressure | Drive disk toward threshold in staging | Alerts fire before PostgreSQL becomes unsafe |
|
||||
| P19 | Replication-slot backlog | Subscriber offline while WAL grows | Retention monitored and bounded |
|
||||
| P20 | Lost slot/WAL | Simulate unrecoverable replication state | Documented resync procedure succeeds |
|
||||
| P21 | High write load | Concurrent writes across all nodes | Lag remains inside target |
|
||||
| P22 | Long soak | 24–72 h realistic traffic | No growing lag/memory/disk/errors |
|
||||
| P23 | ACE comparison | Run table-diff after every failure test | Tables match |
|
||||
| P24 | Backup restore | Restore pgBackRest backup | Database boots and data verified |
|
||||
| P25 | PITR | Recover to timestamp before accidental change | Recovery reaches correct point |
|
||||
| P26 | Entire-site loss | Lose one VM/site completely | Remaining service meets RTO/RPO |
|
||||
| P27 | Credentials/TLS | Invalid clients attempt connection | Unauthorized traffic rejected |
|
||||
| P28 | Upgrade | PostgreSQL/Spock patch upgrade rehearsal | Cluster remains consistent |
|
||||
| P29 | Monitoring failure | Kill exporter/agent | Alert tells you monitoring itself failed |
|
||||
| P30 | Control-plane failure | Stop your FastAPI management service | Database replication continues independently |
|
||||
|
||||
That is the set I would use as your production qualification.
|
||||
|
||||
---
|
||||
|
||||
## 2. The first conflict test: same row, two nodes
|
||||
|
||||
Create:
|
||||
|
||||
```sql
|
||||
CREATE TABLE conflict_test (
|
||||
id UUID PRIMARY KEY,
|
||||
value TEXT,
|
||||
updated_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
```
|
||||
|
||||
Insert one row and allow it to synchronize.
|
||||
|
||||
Then disconnect replication temporarily in your staging test and make:
|
||||
|
||||
Node1:
|
||||
|
||||
```sql
|
||||
UPDATE conflict_test
|
||||
SET value = 'NODE1'
|
||||
WHERE id = '<same-uuid>';
|
||||
```
|
||||
|
||||
Node2:
|
||||
|
||||
```sql
|
||||
UPDATE conflict_test
|
||||
SET value = 'NODE2'
|
||||
WHERE id = '<same-uuid>';
|
||||
```
|
||||
|
||||
Restore connectivity.
|
||||
|
||||
Eventually:
|
||||
|
||||
```sql
|
||||
SELECT * FROM conflict_test;
|
||||
```
|
||||
|
||||
should give the **same value on all three nodes**.
|
||||
|
||||
But the exact winner needs to match your chosen conflict policy.
|
||||
|
||||
Spock 5.x supports policies including:
|
||||
|
||||
```text
|
||||
error
|
||||
apply_remote
|
||||
keep_local
|
||||
last_update_wins
|
||||
```
|
||||
|
||||
with `last_update_wins` using commit timestamps. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/development/configuring/?utm_source=chatgpt.com)
|
||||
|
||||
This test answers:
|
||||
|
||||
> "Does replication converge when two masters genuinely disagree?"
|
||||
|
||||
---
|
||||
|
||||
## 3. Duplicate primary-key test
|
||||
|
||||
This is extremely important.
|
||||
|
||||
While two nodes are isolated:
|
||||
|
||||
Node1:
|
||||
|
||||
```sql
|
||||
INSERT INTO conflict_test(id, value)
|
||||
VALUES ('same-uuid', 'NODE1');
|
||||
```
|
||||
|
||||
Node2:
|
||||
|
||||
```sql
|
||||
INSERT INTO conflict_test(id, value)
|
||||
VALUES ('same-uuid', 'NODE2');
|
||||
```
|
||||
|
||||
Reconnect.
|
||||
|
||||
All nodes should eventually converge according to the configured conflict-resolution policy.
|
||||
|
||||
If you're using generated integer IDs, test those particularly aggressively.
|
||||
|
||||
For active-active, UUID/Snowflake-style distributed IDs are much safer than independent ordinary sequences.
|
||||
|
||||
---
|
||||
|
||||
## 4. Test a second UNIQUE constraint
|
||||
|
||||
This is one of the places active-active systems get interesting.
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
CREATE TABLE user_test (
|
||||
id UUID PRIMARY KEY,
|
||||
email TEXT UNIQUE
|
||||
);
|
||||
```
|
||||
|
||||
Node1:
|
||||
|
||||
```sql
|
||||
INSERT INTO user_test
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
'test@example.com'
|
||||
);
|
||||
```
|
||||
|
||||
Node2 simultaneously:
|
||||
|
||||
```sql
|
||||
INSERT INTO user_test
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
'test@example.com'
|
||||
);
|
||||
```
|
||||
|
||||
Different primary keys:
|
||||
|
||||
```text
|
||||
UUID A ≠ UUID B
|
||||
```
|
||||
|
||||
but identical:
|
||||
|
||||
```text
|
||||
email
|
||||
```
|
||||
|
||||
Spock 5 documentation specifically warns about multiple unique indexes/constraints in multi-master environments because conflict handling can encounter downstream constraint violations. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/v5-0-10/limitations/?utm_source=chatgpt.com)
|
||||
|
||||
You need to know exactly how your application's tables behave here.
|
||||
|
||||
---
|
||||
|
||||
# 5. Network partition — one of your most important tests
|
||||
|
||||
This is different from shutting Node1 down.
|
||||
|
||||
### Clean outage
|
||||
|
||||
```text
|
||||
Node1
|
||||
OFF
|
||||
|
||||
Node2 ←→ Node3
|
||||
```
|
||||
|
||||
Generally easy:
|
||||
|
||||
```text
|
||||
Node2/3 accumulate changes
|
||||
↓
|
||||
Node1 returns
|
||||
↓
|
||||
backlog catches up
|
||||
```
|
||||
|
||||
### Network partition
|
||||
|
||||
Much more dangerous:
|
||||
|
||||
```text
|
||||
Node1 Node2/Node3
|
||||
|
||||
ONLINE ONLINE
|
||||
writes writes
|
||||
|
||||
X network connection X
|
||||
```
|
||||
|
||||
Now both sides are independently accepting transactions.
|
||||
|
||||
Suppose:
|
||||
|
||||
```text
|
||||
Node1:
|
||||
customer balance = 50
|
||||
|
||||
Node2:
|
||||
customer balance = 75
|
||||
```
|
||||
|
||||
After reconnection, Spock has to reconcile those histories.
|
||||
|
||||
Your PASS condition isn't merely:
|
||||
|
||||
```text
|
||||
replication resumed
|
||||
```
|
||||
|
||||
It is:
|
||||
|
||||
```text
|
||||
✓ replication resumed
|
||||
✓ conflict policy executed correctly
|
||||
✓ no subscription disabled
|
||||
✓ exception log understood
|
||||
✓ lag returned to zero
|
||||
✓ ACE says tables agree
|
||||
✓ application business rules still hold
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 6. Test business conflicts, not just database conflicts
|
||||
|
||||
This is possibly **the most important production test**.
|
||||
|
||||
Start with:
|
||||
|
||||
```text
|
||||
inventory = 1
|
||||
```
|
||||
|
||||
During a partition:
|
||||
|
||||
Node1:
|
||||
|
||||
```sql
|
||||
UPDATE products
|
||||
SET inventory = inventory - 1
|
||||
WHERE id = 10;
|
||||
```
|
||||
|
||||
Node2:
|
||||
|
||||
```sql
|
||||
UPDATE products
|
||||
SET inventory = inventory - 1
|
||||
WHERE id = 10;
|
||||
```
|
||||
|
||||
Both transactions locally see:
|
||||
|
||||
```text
|
||||
1 → 0
|
||||
```
|
||||
|
||||
Eventually the database might converge to:
|
||||
|
||||
```text
|
||||
inventory = 0
|
||||
```
|
||||
|
||||
Yet:
|
||||
|
||||
```text
|
||||
two customers bought one item
|
||||
```
|
||||
|
||||
That's a failure at the **business layer**, even though replication technically worked.
|
||||
|
||||
Spock also provides delta-apply mechanisms specifically because counters/running totals are a special problem in active-active designs. [pgEdge Documentation](https://docs.pgedge.com/spock-v6/v6-0-0-beta/conflicts/?utm_source=chatgpt.com)
|
||||
|
||||
For things such as:
|
||||
|
||||
```text
|
||||
bank balances
|
||||
inventory
|
||||
seat availability
|
||||
quotas
|
||||
ticket allocation
|
||||
counters
|
||||
```
|
||||
|
||||
you must test the actual business invariant.
|
||||
|
||||
---
|
||||
|
||||
# 7. Ten-day outage / WAL-retention test
|
||||
|
||||
You don't necessarily have to wait ten real days.
|
||||
|
||||
Generate a representative amount of WAL while one node is offline.
|
||||
|
||||
Monitor:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
slot_name,
|
||||
active,
|
||||
wal_status,
|
||||
pg_size_pretty(
|
||||
pg_wal_lsn_diff(
|
||||
pg_current_wal_lsn(),
|
||||
restart_lsn
|
||||
)
|
||||
) AS retained_wal
|
||||
FROM pg_replication_slots;
|
||||
```
|
||||
|
||||
You want to know:
|
||||
|
||||
```text
|
||||
100 MB backlog?
|
||||
1 GB?
|
||||
20 GB?
|
||||
100 GB?
|
||||
```
|
||||
|
||||
Then calculate how much storage you'd need for your actual outage window.
|
||||
|
||||
Pass condition:
|
||||
|
||||
```text
|
||||
Node offline
|
||||
↓
|
||||
WAL grows
|
||||
↓
|
||||
disk remains safe
|
||||
↓
|
||||
node returns
|
||||
↓
|
||||
lag decreases steadily
|
||||
↓
|
||||
0 lag
|
||||
↓
|
||||
ACE passes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 8. Disk-full test
|
||||
|
||||
You already discovered how unpleasant a `100% /` filesystem can be.
|
||||
|
||||
For production, don't wait until:
|
||||
|
||||
```text
|
||||
100%
|
||||
```
|
||||
|
||||
Alert at something like:
|
||||
|
||||
```text
|
||||
70% warning
|
||||
80% serious
|
||||
90% critical
|
||||
```
|
||||
|
||||
based on your actual capacity model.
|
||||
|
||||
Especially monitor:
|
||||
|
||||
```text
|
||||
/
|
||||
PGDATA
|
||||
pg_wal
|
||||
Docker storage
|
||||
backup repository
|
||||
```
|
||||
|
||||
A lagging replication slot can cause WAL retention to grow dramatically.
|
||||
|
||||
Do the actual "nearly fill the disk" experiment only on staging.
|
||||
|
||||
---
|
||||
|
||||
# 9. DDL testing
|
||||
|
||||
This deserves an entire test category.
|
||||
|
||||
Test:
|
||||
|
||||
```sql
|
||||
CREATE TABLE
|
||||
ALTER TABLE ADD COLUMN
|
||||
ALTER TABLE DROP COLUMN
|
||||
CREATE INDEX
|
||||
DROP INDEX
|
||||
ALTER COLUMN TYPE
|
||||
DROP TABLE
|
||||
```
|
||||
|
||||
But be careful.
|
||||
|
||||
Spock's own v5 documentation says multi-master schema changes require special care because there's no generic "freeze replication queue and wait until every transaction is applied" mechanism. For multi-master, writes should be stopped on all nodes and slots allowed to catch up before incompatible schema changes. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/v5-0-10/limitations/?utm_source=chatgpt.com)
|
||||
|
||||
It also specifically warns about operations including:
|
||||
|
||||
```text
|
||||
CREATE TABLE AS
|
||||
DROP TABLE in 3+ node clusters
|
||||
```
|
||||
|
||||
and recommends maintenance windows for potentially dangerous DDL. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/development/managing/spock_autoddl/?utm_source=chatgpt.com)
|
||||
|
||||
So test your real production migration procedure:
|
||||
|
||||
```text
|
||||
Application maintenance/read-only
|
||||
↓
|
||||
stop writes
|
||||
↓
|
||||
lag → zero
|
||||
↓
|
||||
perform DDL
|
||||
↓
|
||||
verify schema all nodes
|
||||
↓
|
||||
ACE
|
||||
↓
|
||||
resume traffic
|
||||
```
|
||||
|
||||
That procedure itself should be documented.
|
||||
|
||||
---
|
||||
|
||||
# 10. Kill PostgreSQL brutally
|
||||
|
||||
Not just:
|
||||
|
||||
```bash
|
||||
docker stop
|
||||
```
|
||||
|
||||
which is relatively graceful.
|
||||
|
||||
On a disposable/staging node, simulate:
|
||||
|
||||
```text
|
||||
process crash
|
||||
container crash
|
||||
VM crash
|
||||
unexpected host reboot
|
||||
```
|
||||
|
||||
Then verify:
|
||||
|
||||
```text
|
||||
PostgreSQL crash recovery
|
||||
↓
|
||||
Spock starts
|
||||
↓
|
||||
subscriptions reconnect
|
||||
↓
|
||||
backlog catches up
|
||||
↓
|
||||
ACE matches
|
||||
```
|
||||
|
||||
You want proof that an administrator doesn't need to manually rebuild replication after an ordinary crash.
|
||||
|
||||
---
|
||||
|
||||
# 11. Restart nodes repeatedly
|
||||
|
||||
Try:
|
||||
|
||||
```text
|
||||
Restart Node1
|
||||
Restart Node2
|
||||
Restart Node3
|
||||
|
||||
Restart Node1 + Node2
|
||||
Restart Node2 + Node3
|
||||
|
||||
Restart all three
|
||||
```
|
||||
|
||||
Then vary the order in which they come back.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
Node3
|
||||
↓
|
||||
Node1
|
||||
↓
|
||||
Node2
|
||||
```
|
||||
|
||||
The cluster should recover regardless of reasonable startup ordering.
|
||||
|
||||
---
|
||||
|
||||
# 12. Test an actual replication exception
|
||||
|
||||
You should deliberately create something that lands in:
|
||||
|
||||
```sql
|
||||
spock.exception_log
|
||||
```
|
||||
|
||||
Then prove your operational process.
|
||||
|
||||
Monitor:
|
||||
|
||||
```sql
|
||||
SELECT *
|
||||
FROM spock.exception_log
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 50;
|
||||
```
|
||||
|
||||
Spock's exception behavior can be configured to discard an offending statement/transaction or disable a subscription, so your team needs to know exactly which policy you're using and what happens afterward. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/development/troubleshooting/?utm_source=chatgpt.com)
|
||||
|
||||
The test isn't complete until you demonstrate:
|
||||
|
||||
```text
|
||||
alert
|
||||
↓
|
||||
diagnosis
|
||||
↓
|
||||
repair
|
||||
↓
|
||||
subscription healthy
|
||||
↓
|
||||
ACE validation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 13. ACE after EVERY major failure test
|
||||
|
||||
This is important.
|
||||
|
||||
Replication saying:
|
||||
|
||||
```text
|
||||
replicating
|
||||
```
|
||||
|
||||
doesn't prove:
|
||||
|
||||
```text
|
||||
data identical
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
./ace table-diff cluster1 public.your_table
|
||||
```
|
||||
|
||||
ACE compares table contents across nodes and reports actual differences. [pgEdge Documentation](https://docs.pgedge.com/ace/v2-1-1/commands/diff/table-diff/?utm_source=chatgpt.com)
|
||||
|
||||
For each critical table, after:
|
||||
|
||||
```text
|
||||
partition
|
||||
restart
|
||||
conflict
|
||||
DDL
|
||||
recovery
|
||||
upgrade
|
||||
```
|
||||
|
||||
you should end with:
|
||||
|
||||
```text
|
||||
TABLES MATCH
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 14. Heavy-load test
|
||||
|
||||
Your system may work perfectly at:
|
||||
|
||||
```text
|
||||
10 transactions/sec
|
||||
```
|
||||
|
||||
and fall apart at:
|
||||
|
||||
```text
|
||||
2,000 transactions/sec
|
||||
```
|
||||
|
||||
Generate simultaneous:
|
||||
|
||||
```text
|
||||
INSERT
|
||||
UPDATE
|
||||
DELETE
|
||||
SELECT
|
||||
```
|
||||
|
||||
across all three nodes.
|
||||
|
||||
Monitor:
|
||||
|
||||
```sql
|
||||
SELECT * FROM spock.lag_tracker;
|
||||
```
|
||||
|
||||
`spock.lag_tracker` provides replication lag in both bytes and time. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/v5-0-9/monitoring/lag_tracking/?utm_source=chatgpt.com)
|
||||
|
||||
Monitor additionally:
|
||||
|
||||
```text
|
||||
CPU
|
||||
memory
|
||||
disk IOPS
|
||||
network bandwidth
|
||||
connections
|
||||
TPS
|
||||
locks
|
||||
deadlocks
|
||||
WAL/sec
|
||||
replication lag
|
||||
slot backlog
|
||||
```
|
||||
|
||||
You already have much of this in your monitoring/FastAPI work.
|
||||
|
||||
---
|
||||
|
||||
# 15. Resource-exhaustion test
|
||||
|
||||
Check:
|
||||
|
||||
```sql
|
||||
SHOW max_worker_processes;
|
||||
SHOW max_replication_slots;
|
||||
SHOW max_wal_senders;
|
||||
SHOW max_connections;
|
||||
```
|
||||
|
||||
Don't assume:
|
||||
|
||||
```conf
|
||||
max_replication_slots = 10
|
||||
max_wal_senders = 10
|
||||
```
|
||||
|
||||
is automatically a production number just because the lab works.
|
||||
|
||||
Spock uses workers, replication slots, walsenders and replication origins, and insufficient capacity can prevent replication workers from starting. Current pgEdge sizing guidance explicitly warns that tutorial/default values should not be treated as universal production sizing. [pgEdge Documentation](https://docs.pgedge.com/spock-v6/development/sizing/?utm_source=chatgpt.com)
|
||||
|
||||
Run load tests until you know your headroom.
|
||||
|
||||
---
|
||||
|
||||
# 16. Backup is not tested until restore works
|
||||
|
||||
Install/configure pgBackRest and test:
|
||||
|
||||
```text
|
||||
Full backup
|
||||
↓
|
||||
Incremental backup
|
||||
↓
|
||||
WAL archive
|
||||
↓
|
||||
restore
|
||||
```
|
||||
|
||||
Then verify the restored database.
|
||||
|
||||
pgEdge recommends monitoring pgBackRest backups and explicitly emphasizes that disaster-recovery strategy needs to be tested and customized for the database. [pgEdge Documentation](https://docs.pgedge.com/platform/managing/pgbackrest/?utm_source=chatgpt.com)
|
||||
|
||||
---
|
||||
|
||||
# 17. PITR test
|
||||
|
||||
Simulate:
|
||||
|
||||
```text
|
||||
10:00 good data
|
||||
|
||||
10:15 good data
|
||||
|
||||
10:20 accidental DELETE
|
||||
|
||||
10:25 users notice
|
||||
```
|
||||
|
||||
You need to demonstrate:
|
||||
|
||||
```text
|
||||
restore → 10:19
|
||||
```
|
||||
|
||||
pgBackRest supports recovery to a specific timestamp/LSN/XID/recovery point. [pgEdge Documentation](https://docs.pgedge.com/pgbackrest/development/user-guide/pitr/?utm_source=chatgpt.com)
|
||||
|
||||
Measure:
|
||||
|
||||
```text
|
||||
RPO = how much data you lost
|
||||
|
||||
RTO = how long until service returned
|
||||
```
|
||||
|
||||
Don't estimate those numbers.
|
||||
|
||||
Actually measure them.
|
||||
|
||||
---
|
||||
|
||||
# 18. Upgrade test
|
||||
|
||||
Given your older Spock patch level, this is especially important.
|
||||
|
||||
Clone production-like data and perform:
|
||||
|
||||
```text
|
||||
PostgreSQL minor update
|
||||
Spock patch update
|
||||
Docker image update
|
||||
OS security update
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```text
|
||||
restart
|
||||
↓
|
||||
subscriptions healthy
|
||||
↓
|
||||
load test
|
||||
↓
|
||||
conflict test
|
||||
↓
|
||||
ACE
|
||||
```
|
||||
|
||||
Current Spock 5.x patch releases contain fixes specifically involving reconnect/recovery and replication behavior, which are exactly the areas you're testing. [pgEdge Documentation](https://docs.pgedge.com/spock-v5/development/spock_release_notes/?utm_source=chatgpt.com)
|
||||
|
||||
---
|
||||
|
||||
# 19. Security tests
|
||||
|
||||
For production, verify:
|
||||
|
||||
```text
|
||||
✓ PostgreSQL replication traffic encrypted
|
||||
✓ application DB connections encrypted
|
||||
✓ SCRAM/cert authentication
|
||||
✓ pg_hba restricts source networks
|
||||
✓ replication account permissions controlled
|
||||
✓ database superuser credentials aren't in frontend
|
||||
✓ FastAPI secrets aren't hard-coded
|
||||
✓ JWT expires/validates correctly
|
||||
✓ Docker API isn't publicly exposed
|
||||
✓ FastAPI Docker-control endpoints require authorization
|
||||
✓ logs don't leak passwords
|
||||
```
|
||||
|
||||
Your Docker-management API is particularly sensitive because Docker daemon access is effectively host-administrative access.
|
||||
|
||||
---
|
||||
|
||||
# 20. Monitoring failure itself
|
||||
|
||||
People often forget this one.
|
||||
|
||||
Kill:
|
||||
|
||||
```text
|
||||
postgres_exporter
|
||||
Prometheus
|
||||
FastAPI monitoring agent
|
||||
```
|
||||
|
||||
Your system should tell you:
|
||||
|
||||
> Monitoring is unavailable.
|
||||
|
||||
Otherwise this can happen:
|
||||
|
||||
```text
|
||||
Monitoring dies
|
||||
↓
|
||||
everything looks green
|
||||
↓
|
||||
database fails
|
||||
↓
|
||||
nobody knew
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build these six dashboards
|
||||
|
||||
For production I would make your FastAPI/React monitoring page show:
|
||||
|
||||
```text
|
||||
CLUSTER HEALTH
|
||||
|
||||
Node1 ONLINE
|
||||
Node2 ONLINE
|
||||
Node3 ONLINE
|
||||
|
||||
REPLICATION
|
||||
|
||||
Node1 → Node2 0 MB / 20 ms
|
||||
Node1 → Node3 0 MB / 25 ms
|
||||
Node2 → Node1 0 MB / 18 ms
|
||||
Node2 → Node3 0 MB / 21 ms
|
||||
Node3 → Node1 0 MB / 23 ms
|
||||
Node3 → Node2 0 MB / 19 ms
|
||||
|
||||
SLOTS
|
||||
|
||||
slot active retained WAL
|
||||
node1_from_node2 yes 0 MB
|
||||
node1_from_node3 yes 0 MB
|
||||
|
||||
CONFLICTS
|
||||
|
||||
last hour: 0
|
||||
|
||||
EXCEPTIONS
|
||||
|
||||
unresolved: 0
|
||||
|
||||
SYSTEM
|
||||
|
||||
CPU 28%
|
||||
RAM 61%
|
||||
Disk 52%
|
||||
Connections 42 / 200
|
||||
WAL 3.1 GB
|
||||
|
||||
ACE
|
||||
|
||||
Last verification:
|
||||
✓ TABLES MATCH
|
||||
```
|
||||
|
||||
That would turn your FastAPI project into a genuinely useful management plane rather than just an API collection.
|
||||
|
||||
---
|
||||
|
||||
# Your final go-live gate
|
||||
|
||||
I would **not call the system production-ready** until you can demonstrate all of these:
|
||||
|
||||
1. Every node can write and replicate to every other node.
|
||||
2. A node can disappear and automatically catch up.
|
||||
3. A writable network partition has been tested.
|
||||
4. Concurrent-write conflicts behave predictably.
|
||||
5. Application business invariants survive concurrent writes.
|
||||
6. WAL backlog cannot silently fill the disk.
|
||||
7. Replication errors generate alerts.
|
||||
8. DDL has a documented maintenance procedure.
|
||||
9. ACE confirms convergence after failures.
|
||||
10. Backup restore actually works.
|
||||
11. PITR actually works.
|
||||
12. RPO and RTO have been measured.
|
||||
13. High-load and long-duration tests pass.
|
||||
14. Security/TLS/authentication have been verified.
|
||||
15. Upgrade and rollback procedures have been rehearsed.
|
||||
16. Every failure has a written recovery runbook.
|
||||
|
||||
If those pass repeatedly, then you're getting into **actual production-grade active-active territory**, rather than simply having a working three-node Spock cluster.
|
||||
Reference in new issue
Block a user