claudify-agent 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/QUICK-REFERENCE.md +213 -0
- package/.claude/cost-dashboard.md +162 -0
- package/.claude/docs/COST-OPTIMIZATION.md +403 -0
- package/.claude/docs/MCP-INTEGRATION-GUIDE.md +348 -0
- package/.claude/docs/PLAN-MODE-OPTIMIZATION.md +836 -0
- package/.claude/hooks/auto-learning-review.ts +195 -0
- package/.claude/hooks/auto-lint-fix.ts +104 -0
- package/.claude/hooks/auto-security-review.ts +354 -0
- package/.claude/hooks/auto-validate.ts +348 -0
- package/.claude/hooks/plan-quality-tracker.ts +719 -0
- package/.claude/rules/anti-patterns.md +22 -0
- package/.claude/rules/context-tier-1.md +41 -0
- package/.claude/rules/context-tier-2.md +285 -0
- package/.claude/rules/general.md +25 -0
- package/.claude/rules/mcp-auto-use.md +46 -0
- package/.claude/rules/parallel-exploration-auto.md +31 -0
- package/.claude/rules/plan-mode-execution.md +289 -0
- package/.claude/rules/plan-mode-research.md +34 -0
- package/.claude/rules/plan-mode-structure.md +166 -0
- package/.claude/rules/plan-mode-validation.md +130 -0
- package/.claude/rules/security-critical.md +19 -0
- package/.claude/rules/tool-auto-selection.md +45 -0
- package/.claude/rules/tool-strategy.md +22 -0
- package/.claude/scripts/auto-validate.js +69 -0
- package/.claude/scripts/security-scan.js +45 -0
- package/.claude/security-dashboard.md +142 -0
- package/.claude/skills/backend-concurrency/SKILL.md +203 -0
- package/.claude/skills/debug-optimizer/SKILL.md +399 -0
- package/.claude/skills/debug-optimizer.md +12 -0
- package/.claude/skills/golang-guide/SKILL.md +177 -0
- package/.claude/skills/learning-loop/SKILL.md +308 -0
- package/.claude/skills/mcp-mastery/SKILL.md +214 -0
- package/.claude/skills/nestjs-guide/SKILL.md +197 -0
- package/.claude/skills/nextjs-guide/SKILL.md +687 -0
- package/.claude/skills/nextjs.md +13 -0
- package/.claude/skills/parallel-exploration/SKILL.md +193 -0
- package/.claude/skills/plan-mode-mastery/SKILL.md +263 -0
- package/.claude/skills/python-guide/SKILL.md +822 -0
- package/.claude/skills/react-guide/SKILL.md +762 -0
- package/.claude/skills/react.md +12 -0
- package/.claude/skills/security.md +18 -0
- package/.claude/skills/solidity-guide/SKILL.md +32 -0
- package/.claude/skills/tool-selection/SKILL.md +428 -0
- package/.claude/skills/vite-guide/SKILL.md +445 -0
- package/CLAUDE.md +49 -0
- package/README.md +65 -2
- package/bin/cli.js +50 -0
- package/package.json +44 -4
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Multi-Language Auto-Validate Hook
|
|
5
|
+
* Supports TypeScript/JS, Go, and Solidity (Foundry/Hardhat)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
|
|
11
|
+
console.log('⚡ Running Claudify Multi-Language Verification Pipeline...\n');
|
|
12
|
+
|
|
13
|
+
let hasErrors = false;
|
|
14
|
+
|
|
15
|
+
function runStep(name, command) {
|
|
16
|
+
process.stdout.write(`⏳ [Check] ${name}... `);
|
|
17
|
+
try {
|
|
18
|
+
execSync(command, { stdio: 'pipe' });
|
|
19
|
+
console.log('✅ PASSED');
|
|
20
|
+
return true;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.log('❌ FAILED');
|
|
23
|
+
console.error(`\nError Output for ${name}:\n`, error.stdout?.toString() || error.message);
|
|
24
|
+
hasErrors = true;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 1. Golang Pipeline
|
|
30
|
+
if (existsSync('go.mod')) {
|
|
31
|
+
console.log('🐹 Detected Go Project');
|
|
32
|
+
runStep('Go Vet Static Analysis', 'go vet ./...');
|
|
33
|
+
try {
|
|
34
|
+
execSync('golangci-lint --version', { stdio: 'ignore' });
|
|
35
|
+
runStep('GolangCI-Lint', 'golangci-lint run ./...');
|
|
36
|
+
} catch {
|
|
37
|
+
// Optional if not installed
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. Solidity / Smart Contract Pipeline
|
|
42
|
+
if (existsSync('foundry.toml')) {
|
|
43
|
+
console.log('⛓️ Detected Foundry Smart Contract Project');
|
|
44
|
+
runStep('Foundry Build & Compilation', 'forge build');
|
|
45
|
+
runStep('Foundry Tests', 'forge test');
|
|
46
|
+
} else if (existsSync('hardhat.config.js') || existsSync('hardhat.config.ts')) {
|
|
47
|
+
console.log('⛓️ Detected Hardhat Smart Contract Project');
|
|
48
|
+
runStep('Hardhat Compile', 'npx hardhat compile');
|
|
49
|
+
runStep('Hardhat Test', 'npx hardhat test');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 3. Node.js / TypeScript Pipeline
|
|
53
|
+
if (existsSync('package.json')) {
|
|
54
|
+
if (existsSync('eslint.config.js') || existsSync('.eslintrc.json')) {
|
|
55
|
+
runStep('ESLint Syntax & Formatting', 'npm run lint');
|
|
56
|
+
}
|
|
57
|
+
if (existsSync('tsconfig.json')) {
|
|
58
|
+
runStep('TypeScript Type Check', 'npm run typecheck');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('\n----------------------------------------');
|
|
63
|
+
if (hasErrors) {
|
|
64
|
+
console.error('❌ Verification failed. Fix issues before concluding the task.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
} else {
|
|
67
|
+
console.log('🎉 All language checks passed cleanly (0 LLM tokens spent)!');
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Security & Secret Leak Scanner
|
|
5
|
+
* Scans local workspace for hardcoded secrets before committing or sending to LLMs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
|
|
10
|
+
console.log('🛡️ Running Claudify Security & Secret Scanner...\n');
|
|
11
|
+
|
|
12
|
+
const DANGEROUS_PATTERNS = [
|
|
13
|
+
{ name: 'AWS Access Key', regex: 'AKIA[0-9A-Z]{16}' },
|
|
14
|
+
{ name: 'Generic API Secret', regex: 'api_secret[\\s]*=[\\s]*["\'][a-zA-Z0-9_-]{16,}["\']' },
|
|
15
|
+
{ name: 'Private Key Block', regex: '-----BEGIN [A-Z ]+ PRIVATE KEY-----' },
|
|
16
|
+
{ name: 'Hardcoded Password Assignment', regex: 'password[\\s]*=[\\s]*["\'][^"\']{6,}["\']' },
|
|
17
|
+
{ name: 'GitHub Personal Token', regex: 'ghp_[0-9a-zA-Z]{36}' }
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
let leakFound = false;
|
|
21
|
+
|
|
22
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
23
|
+
try {
|
|
24
|
+
// Search in git-tracked or current files, excluding node_modules and .git
|
|
25
|
+
const grepCmd = process.platform === 'win32'
|
|
26
|
+
? `git grep -E -I -i "${pattern.regex}" 2>$null`
|
|
27
|
+
: `git grep -E -I -i "${pattern.regex}" 2>/dev/null`;
|
|
28
|
+
|
|
29
|
+
const result = execSync(grepCmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
30
|
+
if (result) {
|
|
31
|
+
console.warn(`⚠️ Potential secret detected (${pattern.name}):\n${result}\n`);
|
|
32
|
+
leakFound = true;
|
|
33
|
+
}
|
|
34
|
+
} catch (_e) {
|
|
35
|
+
// Exit code 1 means pattern not found (safe)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (leakFound) {
|
|
40
|
+
console.error('❌ SECURITY ALERT: Potential secrets detected in local code! Move them to .env before continuing.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
} else {
|
|
43
|
+
console.log('✅ Clean! No leaked secrets or dangerous credentials found.');
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Security Dashboard
|
|
2
|
+
|
|
3
|
+
**Last Updated**: Auto-generated after each security scan
|
|
4
|
+
**Scan Frequency**: After every code change
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Recent Scans (Last 10)
|
|
9
|
+
|
|
10
|
+
| # | Timestamp | Files Changed | Vulnerabilities | Severity | Status |
|
|
11
|
+
|---|-----------|---------------|-----------------|----------|--------|
|
|
12
|
+
| 1 | - | - | - | - | Pending first scan |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Vulnerability Trends
|
|
17
|
+
|
|
18
|
+
### This Week
|
|
19
|
+
- **Total Scans**: 0
|
|
20
|
+
- **Vulnerabilities Found**: 0
|
|
21
|
+
- **Critical**: 0
|
|
22
|
+
- **High**: 0
|
|
23
|
+
- **Medium**: 0
|
|
24
|
+
- **Low**: 0
|
|
25
|
+
|
|
26
|
+
### Trend Analysis
|
|
27
|
+
- **Vulnerability Rate**: 0% (target: <5%)
|
|
28
|
+
- **Detection Accuracy**: >80% (OWASP Top 10)
|
|
29
|
+
- **False Positive Rate**: Tracking...
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Common Patterns Detected
|
|
34
|
+
|
|
35
|
+
### Critical/High Severity
|
|
36
|
+
1. **Hardcoded Credentials**
|
|
37
|
+
- Pattern: `password = "..."`, `api_key = '...'`
|
|
38
|
+
- Fix: Use environment variables
|
|
39
|
+
- Occurrences: 0
|
|
40
|
+
|
|
41
|
+
2. **SQL Injection**
|
|
42
|
+
- Pattern: Unparameterized queries
|
|
43
|
+
- Fix: Use parameterized queries or ORM
|
|
44
|
+
- Occurrences: 0
|
|
45
|
+
|
|
46
|
+
3. **XSS (Cross-Site Scripting)**
|
|
47
|
+
- Pattern: Unsanitized user input in HTML
|
|
48
|
+
- Fix: Sanitize with DOMPurify or similar
|
|
49
|
+
- Occurrences: 0
|
|
50
|
+
|
|
51
|
+
4. **Insecure Crypto**
|
|
52
|
+
- Pattern: MD5, SHA1, weak algorithms
|
|
53
|
+
- Fix: Use SHA-256+, bcrypt, argon2
|
|
54
|
+
- Occurrences: 0
|
|
55
|
+
|
|
56
|
+
5. **Path Traversal**
|
|
57
|
+
- Pattern: Unvalidated file paths
|
|
58
|
+
- Fix: Validate and sanitize paths
|
|
59
|
+
- Occurrences: 0
|
|
60
|
+
|
|
61
|
+
6. **Command Injection**
|
|
62
|
+
- Pattern: Shell execution with user input
|
|
63
|
+
- Fix: Avoid shell, use safe APIs
|
|
64
|
+
- Occurrences: 0
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## False Positive Tracking
|
|
69
|
+
|
|
70
|
+
| Date | Pattern | Reason | Action Taken |
|
|
71
|
+
|------|---------|--------|--------------|
|
|
72
|
+
| - | - | - | - |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Security Health Score
|
|
77
|
+
|
|
78
|
+
**Current Score**: 100/100 (No vulnerabilities detected)
|
|
79
|
+
|
|
80
|
+
**Calculation**:
|
|
81
|
+
- Base score: 100
|
|
82
|
+
- Critical vulnerabilities: -20 each
|
|
83
|
+
- High vulnerabilities: -10 each
|
|
84
|
+
- Medium vulnerabilities: -5 each
|
|
85
|
+
- Low vulnerabilities: -2 each
|
|
86
|
+
|
|
87
|
+
**Target**: >90/100
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Auto-Scan Configuration
|
|
92
|
+
|
|
93
|
+
**Enabled**: ✅ Yes
|
|
94
|
+
**Trigger**: After every code change
|
|
95
|
+
**Severity Threshold**: High
|
|
96
|
+
**Scan Patterns**:
|
|
97
|
+
- ✅ Hardcoded credentials
|
|
98
|
+
- ✅ SQL injection
|
|
99
|
+
- ✅ XSS
|
|
100
|
+
- ✅ Insecure crypto
|
|
101
|
+
- ✅ Path traversal
|
|
102
|
+
- ✅ Command injection
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Action Items
|
|
107
|
+
|
|
108
|
+
### Immediate (Critical/High found)
|
|
109
|
+
- [ ] None
|
|
110
|
+
|
|
111
|
+
### This Week (Medium found)
|
|
112
|
+
- [ ] None
|
|
113
|
+
|
|
114
|
+
### Review (Low/Info found)
|
|
115
|
+
- [ ] None
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Related Documentation
|
|
120
|
+
|
|
121
|
+
- `.cursor/hooks/auto-security-review.ts` - Security scan hook
|
|
122
|
+
- `.cursor/rules/security-critical.md` - Security rules
|
|
123
|
+
- `.cursor/hooks.config.json` - Configuration
|
|
124
|
+
- `AGENT-INSTRUCTION-BEST-PRACTICES.md` - Security research (AppSec Santa findings)
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Maintenance
|
|
129
|
+
|
|
130
|
+
**Weekly Review**:
|
|
131
|
+
- [ ] Review false positives
|
|
132
|
+
- [ ] Update scan patterns if needed
|
|
133
|
+
- [ ] Check detection accuracy
|
|
134
|
+
|
|
135
|
+
**Monthly Review**:
|
|
136
|
+
- [ ] Analyze vulnerability trends
|
|
137
|
+
- [ ] Update severity thresholds
|
|
138
|
+
- [ ] Add new OWASP Top 10 patterns
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
*This dashboard is automatically updated by the auto-security-review hook*
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: backend-concurrency
|
|
3
|
+
description: Deep dive guide for concurrency control, distributed locking, transaction isolation, race conditions, deadlocks, and idempotency in high-load backend systems.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Backend Concurrency, Locking & Data Consistency Guide
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
When architecting concurrent backend services:
|
|
10
|
+
1. **Never Check-Then-Act**: Eliminate race conditions at the database level using atomic updates.
|
|
11
|
+
2. **Deterministic Lock Ordering**: Always acquire locks in numeric/lexicographical order to avoid deadlocks.
|
|
12
|
+
3. **Short-Lived Transactions**: Never perform external HTTP, third-party RPCs, or file I/O inside database transactions.
|
|
13
|
+
4. **Idempotency by Design**: Every state-changing API or message consumer must enforce deduplication via unique keys.
|
|
14
|
+
5. **Right Isolation Level**: Choose Read Committed for standard CRUD, Serializable/Pessimistic for financial ledger balance updates.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Concurrency Anomalies & Data Hazards
|
|
19
|
+
|
|
20
|
+
### Lost Updates (The Classical Race Condition)
|
|
21
|
+
Occurs when two concurrent transactions read the same initial state, compute a new value, and overwrite each other's changes.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Request A: Read Balance ($100)
|
|
25
|
+
Request B: Read Balance ($100)
|
|
26
|
+
Request A: Compute 100 - 40 = $60 --> Write Balance ($60)
|
|
27
|
+
Request B: Compute 100 - 30 = $70 --> Write Balance ($70) (A's update lost!)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Solution 1: Single Atomic Query (Best Performance)
|
|
31
|
+
```sql
|
|
32
|
+
-- Safe under concurrent execution; the database engine guarantees atomic row-level serialization
|
|
33
|
+
UPDATE accounts
|
|
34
|
+
SET balance = balance - :amount, updated_at = NOW()
|
|
35
|
+
WHERE id = :id AND balance >= :amount;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### Solution 2: Optimistic Concurrency Control (OCC)
|
|
39
|
+
Best for read-heavy workloads with low-to-medium contention:
|
|
40
|
+
```sql
|
|
41
|
+
-- 1. Read record with current version
|
|
42
|
+
SELECT id, balance, version FROM accounts WHERE id = 123; -- version = 5
|
|
43
|
+
|
|
44
|
+
-- 2. Update asserting the version hasn't drifted
|
|
45
|
+
UPDATE accounts
|
|
46
|
+
SET balance = :newBalance, version = version + 1
|
|
47
|
+
WHERE id = 123 AND version = 5;
|
|
48
|
+
|
|
49
|
+
-- If rows affected == 0, another request intervened. Abort or retry with exponential jitter.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 2. Pessimistic Locking & Safe Transaction Boundaries
|
|
55
|
+
|
|
56
|
+
### `SELECT FOR UPDATE` Pattern
|
|
57
|
+
Use when calculations are complex and cannot be expressed in a single atomic SQL statement:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// TypeORM / NestJS Example
|
|
61
|
+
await dataSource.transaction(async (manager) => {
|
|
62
|
+
// 1. Lock the row exclusively until the transaction commits
|
|
63
|
+
const account = await manager.findOne(Account, {
|
|
64
|
+
where: { id: accountId },
|
|
65
|
+
lock: { mode: 'pessimistic_write' }, // SELECT ... FOR UPDATE
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (account.balance < amount) {
|
|
69
|
+
throw new BadRequestException('Insufficient funds');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
account.balance -= amount;
|
|
73
|
+
await manager.save(account);
|
|
74
|
+
|
|
75
|
+
// 2. Insert audit log within the same transaction boundary
|
|
76
|
+
await manager.insert(AuditLog, {
|
|
77
|
+
accountId,
|
|
78
|
+
amount: -amount,
|
|
79
|
+
action: 'WITHDRAWAL',
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### ⚠️ The Cardinal Sin: I/O inside Database Locks
|
|
85
|
+
```typescript
|
|
86
|
+
// ❌ HORRIBLE: Holding database lock during network latency
|
|
87
|
+
await queryRunner.startTransaction();
|
|
88
|
+
const account = await queryRunner.manager.findOne(Account, { lock: { mode: 'pessimistic_write' } });
|
|
89
|
+
|
|
90
|
+
// DANGER: If Stripe takes 3 seconds, all other requests to this account (and connection pool) FREEZE!
|
|
91
|
+
await stripe.charges.create({ ... });
|
|
92
|
+
|
|
93
|
+
await queryRunner.commitTransaction();
|
|
94
|
+
|
|
95
|
+
// ✅ CORRECT: Do external I/O before or after database transaction locks!
|
|
96
|
+
const preAuth = await stripe.paymentIntents.create({ ... });
|
|
97
|
+
await dataSource.transaction(async (manager) => {
|
|
98
|
+
// Ultra-fast DB update in milliseconds
|
|
99
|
+
await manager.update(Account, id, { ... });
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 3. Distributed Locking (Redis / Redlock)
|
|
106
|
+
|
|
107
|
+
When concurrency spans multiple service nodes, database row locks may be insufficient (e.g., rate-limiting, preventing multiple cron workers from firing, external API quota limits).
|
|
108
|
+
|
|
109
|
+
### Safe Redis Lock Pattern
|
|
110
|
+
```typescript
|
|
111
|
+
import { Redis } from 'ioredis';
|
|
112
|
+
import { randomUUID } from 'crypto';
|
|
113
|
+
|
|
114
|
+
export class DistributedLockService {
|
|
115
|
+
constructor(private readonly redis: Redis) {}
|
|
116
|
+
|
|
117
|
+
async acquireLock(resource: string, ttlMs: number): Promise<string | null> {
|
|
118
|
+
const lockToken = randomUUID();
|
|
119
|
+
// NX: Only set if not exists, PX: Millisecond expiration
|
|
120
|
+
const acquired = await this.redis.set(`lock:${resource}`, lockToken, 'PX', ttlMs, 'NX');
|
|
121
|
+
return acquired === 'OK' ? lockToken : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async releaseLock(resource: string, lockToken: string): Promise<boolean> {
|
|
125
|
+
// Lua script guarantees that we ONLY delete the key if value matches our token!
|
|
126
|
+
// Prevents deleting someone else's lock if ours expired due to unexpected lag.
|
|
127
|
+
const luaScript = `
|
|
128
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
129
|
+
return redis.call("del", KEYS[1])
|
|
130
|
+
else
|
|
131
|
+
return 0
|
|
132
|
+
end
|
|
133
|
+
`;
|
|
134
|
+
const result = await this.redis.eval(luaScript, 1, `lock:${resource}`, lockToken);
|
|
135
|
+
return result === 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 4. Deadlock Prevention: Deterministic Ordering
|
|
143
|
+
|
|
144
|
+
### The Problem
|
|
145
|
+
- **Transaction 1:** Locks Account 1 $\rightarrow$ Attempts to lock Account 2.
|
|
146
|
+
- **Transaction 2:** Locks Account 2 $\rightarrow$ Attempts to lock Account 1.
|
|
147
|
+
- **Result:** Mutual deadlock. The DB engine terminates one transaction with an error.
|
|
148
|
+
|
|
149
|
+
### The Solution: Global Lock Ordering
|
|
150
|
+
Always sort resource identifiers prior to locking:
|
|
151
|
+
|
|
152
|
+
```go
|
|
153
|
+
// Go implementation for multi-account transfer
|
|
154
|
+
func TransferFunds(ctx context.Context, db *sql.DB, fromID, toID int64, amount float64) error {
|
|
155
|
+
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
|
|
156
|
+
if err != nil {
|
|
157
|
+
return err
|
|
158
|
+
}
|
|
159
|
+
defer tx.Rollback()
|
|
160
|
+
|
|
161
|
+
// Deterministic Lock Ordering: always lock smaller ID first
|
|
162
|
+
firstID, secondID := fromID, toID
|
|
163
|
+
if firstID > secondID {
|
|
164
|
+
firstID, secondID = toID, fromID
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Acquire lock 1
|
|
168
|
+
if _, err := tx.ExecContext(ctx, "SELECT id FROM accounts WHERE id = $1 FOR UPDATE", firstID); err != nil {
|
|
169
|
+
return err
|
|
170
|
+
}
|
|
171
|
+
// Acquire lock 2
|
|
172
|
+
if _, err := tx.ExecContext(ctx, "SELECT id FROM accounts WHERE id = $1 FOR UPDATE", secondID); err != nil {
|
|
173
|
+
return err
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Perform balances updates safely...
|
|
177
|
+
return tx.Commit()
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 5. Idempotent Consumer & Deduplication
|
|
184
|
+
|
|
185
|
+
For Kafka consumers, RabbitMQ listeners, and Webhook handlers:
|
|
186
|
+
```
|
|
187
|
+
Message Arrives --> Check Redis for MessageID
|
|
188
|
+
├─ If Exists: Acknowledge & Ignore (Duplicate)
|
|
189
|
+
└─ If Not Exists:
|
|
190
|
+
Set MessageID in Redis (TTL = 24h)
|
|
191
|
+
Process Business Logic
|
|
192
|
+
Ack Message
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```sql
|
|
196
|
+
-- Database Level Idempotency Ledger
|
|
197
|
+
CREATE TABLE idempotency_keys (
|
|
198
|
+
key VARCHAR(255) PRIMARY KEY,
|
|
199
|
+
response_body JSONB NOT NULL,
|
|
200
|
+
status_code INT NOT NULL,
|
|
201
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
202
|
+
);
|
|
203
|
+
```
|