feat: Deploy HA auto-failover with database promotion

- Enhanced DNS failover monitor on secondary (72.62.39.24)
- Auto-promotes database: pg_ctl promote on failover
- Creates DEMOTED flag on primary via SSH (split-brain protection)
- Telegram notifications with database promotion status
- Startup safety script ready (integration pending)
- 90-second automatic recovery vs 10-30 min manual
- Zero-cost 95% enterprise HA benefit

Status: DEPLOYED and MONITORING (14:52 CET)
Next: Controlled failover test during maintenance
This commit is contained in:
mindesbunister
2025-12-12 15:54:03 +01:00
parent 7ff5c5b3a4
commit d637aac2d7
25 changed files with 1071 additions and 170 deletions

View File

@@ -55,7 +55,7 @@ describe('Bug #76: Exit Orders Validation', () => {
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
tp2SizePercent: 25,
direction: 'long',
useDualStops: false
}
@@ -67,6 +67,35 @@ describe('Bug #76: Exit Orders Validation', () => {
expect(result.success).toBe(true)
expect(result.signatures).toHaveLength(3)
expect(result.signatures).toEqual(['TP1_SIG', 'TP2_SIG', 'SL_SIG'])
expect(result.expectedOrders).toBe(3)
expect(result.placedOrders).toBe(3)
})
it('should return success when TP2 is trigger-only (no on-chain TP2)', async () => {
// Only TP1 + SL should be placed when tp2SizePercent is 0
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('SL_SIG') // SL
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: false
}
const result = await placeExitOrders(options)
expect(result.success).toBe(true)
expect(result.signatures).toEqual(['TP1_SIG', 'SL_SIG'])
expect(result.expectedOrders).toBe(2)
expect(result.placedOrders).toBe(2)
})
it('should return failure when SL placement fails', async () => {
@@ -84,7 +113,7 @@ describe('Bug #76: Exit Orders Validation', () => {
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
tp2SizePercent: 25,
direction: 'long',
useDualStops: false
}
@@ -113,7 +142,7 @@ describe('Bug #76: Exit Orders Validation', () => {
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
tp2SizePercent: 25,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,
@@ -125,6 +154,37 @@ describe('Bug #76: Exit Orders Validation', () => {
expect(result.success).toBe(true)
expect(result.signatures).toHaveLength(4)
expect(result.signatures).toEqual(['TP1_SIG', 'TP2_SIG', 'SOFT_SIG', 'HARD_SIG'])
expect(result.expectedOrders).toBe(4)
expect(result.placedOrders).toBe(4)
})
it('should return success with trigger-only TP2 (TP1 + Soft + Hard)', async () => {
mockDriftClient.placePerpOrder
.mockResolvedValueOnce('TP1_SIG') // TP1
.mockResolvedValueOnce('SOFT_SIG') // Soft Stop
.mockResolvedValueOnce('HARD_SIG') // Hard Stop
const options: PlaceExitOrdersOptions = {
symbol: 'SOL-PERP',
positionSizeUSD: 8000,
entryPrice: 140.00,
tp1Price: 141.20,
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,
hardStopPrice: 138.50
}
const result = await placeExitOrders(options)
expect(result.success).toBe(true)
expect(result.signatures).toEqual(['TP1_SIG', 'SOFT_SIG', 'HARD_SIG'])
expect(result.expectedOrders).toBe(3)
expect(result.placedOrders).toBe(3)
})
it('should return failure when soft stop fails', async () => {
@@ -141,7 +201,7 @@ describe('Bug #76: Exit Orders Validation', () => {
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
tp2SizePercent: 25,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,
@@ -169,7 +229,7 @@ describe('Bug #76: Exit Orders Validation', () => {
tp2Price: 142.41,
stopLossPrice: 138.71,
tp1SizePercent: 75,
tp2SizePercent: 0,
tp2SizePercent: 25,
direction: 'long',
useDualStops: true,
softStopPrice: 139.00,