08/05/2026 06:52am

JS2GO EP.45 Transaction Management and ACID in Go vs JavaScript
#Transactions
#JavaScript
#ACID
#Golang
#Go
#Node.js
A transaction is a group of operations that must all succeed together or fail together.
They are essential for any system that requires 100% data accuracy, such as:
- Deducting balance + writing transaction logs + updating stock
- Transferring money between accounts
- Moving data from “Order” → “Shipment”
- Updating several tables in one workflow
If any single operation fails, the system must ROLLBACK everything otherwise the data becomes inconsistent, causing serious long-term issues.
In this episode, you will learn:
✔ ACID Properties
✔ How to handle Deadlocks
✔ Go Transaction Patterns (pgx + GORM)
✔ Node.js Transaction Patterns (pg + PostgreSQL)
✔ SQL Transactions vs Business Transactions
✔ Production Best Practices
⭐ 1) ACID Properties The Foundation of Reliable Systems
ACID represents the 4 essential guarantees of any safe and consistent transaction.
| Property | Meaning |
|---|---|
| Atomicity | All operations succeed or none do |
| Consistency | Data remains valid and follows constraints after commit |
| Isolation | Transactions should not interfere with one another |
| Durability | Once committed, data is permanently stored |
💡 Example: Transfer 1,000 THB
- Deduct from Account A
- Add to Account B
- Write log
If step #2 fails → rollback everything. This is why ACID is critical for finance, e-commerce, logistics, and enterprise systems.
⭐ 2) Universal SQL Transaction Flow
Works with PostgreSQL and MySQL:
BEGIN;
-- operations
COMMIT;
-- or
ROLLBACK;
Simple but extremely powerful.
⭐ 3) Isolation Levels The Most Overlooked Setting
SQL defines 4 isolation levels:
| Level | Prevents | Notes |
|---|---|---|
| Read Uncommitted | — | Rarely used |
| Read Committed | Dirty Reads | Default in PostgreSQL |
| Repeatable Read | Non-repeatable Reads | Safer, more strict |
| Serializable | Phantom Reads | Safest but more deadlocks |
PostgreSQL’s default (READ COMMITTED) works for 90% of production workloads.
⭐ 4) What Is a Deadlock?
A deadlock happens when two transactions wait for each other forever.
| T1 | T2 |
|---|---|
| Locks Row A | |
| Locks Row B | |
| Tries to lock Row B → waits | |
| Tries to lock Row A → waits |
Both transactions freeze.
✔ How to fix deadlocks:
- Detect error code & retry
- Apply consistent lock ordering
- Use
SELECT ... FOR UPDATEcarefully - Set statement timeout
⭐ 5) SQL Transaction vs Business Transaction
SQL Transaction
Happens within one database:
- Money transfer
- Update multiple fields
- CRUD operations
- Stock reservation
Business Transaction
Involves multiple services, e.g.:
- Payment
- Inventory
- Shipping
- Email / Notification
This requires advanced patterns:
✔ Saga Pattern
✔ Outbox Pattern
✔ Event-driven architecture
In EP.45 we focus mainly on SQL transactions.
⭐ 6) Transactions in Go (pgx)
pgx is the fastest and most production-ready PostgreSQL driver for Go.
🔹 Basic Transaction (pgx)
tx, err := DB.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
_, err = tx.Exec(ctx, "UPDATE users SET ...")
if err != nil {
return err
}
return tx.Commit(ctx)
🔹 Full Transaction with Deadlock Retry (Recommended)
func Transfer(ctx context.Context, from, to string, amount int) error {
for retry := 0; retry < 3; retry++ {
tx, err := DB.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
// Step 1: Deduct
_, err = tx.Exec(ctx,
"UPDATE accounts SET balance = balance - $1 WHERE id=$2",
amount, from,
)
if err != nil {
continue // Retry on deadlock
}
// Step 2: Add
_, err = tx.Exec(ctx,
"UPDATE accounts SET balance = balance + $1 WHERE id=$2",
amount, to,
)
if err != nil {
continue
}
if err := tx.Commit(ctx); err == nil {
return nil
}
}
return errors.New("transaction failed after retries")
}
This is the gold standard for mission-critical systems.
⭐ 7) Transactions in Go (GORM)
GORM makes transactions extremely easy:
func Transfer(db *gorm.DB, from, to string, amount int) error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&Account{}).
Where("id = ?", from).
Update("balance", gorm.Expr("balance - ?", amount)).Error; err != nil {
return err
}
if err := tx.Model(&Account{}).
Where("id = ?", to).
Update("balance", gorm.Expr("balance + ?", amount)).Error; err != nil {
return err
}
return nil
})
}
GORM automatically handles commit/rollback.
⭐ 8) Transactions in Node.js (pg)
🔹 Basic Transaction
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query(
"UPDATE accounts SET balance = balance - $1 WHERE id=$2",
[1000, "A"]
);
await client.query(
"UPDATE accounts SET balance = balance + $1 WHERE id=$2",
[1000, "B"]
);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
}
🔹 Transaction with Deadlock Retry (Recommended)
async function transfer(from, to, amount) {
for (let attempt = 0; attempt < 3; attempt++) {
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query(
"UPDATE accounts SET balance = balance - $1 WHERE id=$2",
[amount, from]
);
await client.query(
"UPDATE accounts SET balance = balance + $1 WHERE id=$2",
[amount, to]
);
await client.query("COMMIT");
return;
} catch (err) {
await client.query("ROLLBACK");
if (err.code === "40P01") continue; // Deadlock
throw err;
} finally {
client.release();
}
}
throw new Error("transaction failed after retries");
}
Equivalent to Go’s retry pattern.
⭐ 9) Transaction Scopes
| Scope | When to Use |
|---|---|
| Single Query | No transaction needed |
| Simple multi-step logic | Use SQL transaction |
| Complex domain logic | Encapsulate in a service function |
| Distributed workflow | Use Saga / Outbox |
⭐ 10) Production Best Practices
✔ Set statement timeout — prevent long-running locks
✔ Detect & retry deadlocks (error code 40P01)
✔ Keep transactions short-lived
✔ Log commit/rollback
✔ Implement idempotency for retryable operations
✔ Avoid unnecessary SELECT FOR UPDATE
📌 Summary
Transactions protect your system from inconsistent data and catastrophic errors.
In this episode, you learned:
✔ ACID & Isolation Levels
✔ Deadlock Detection & Retry
✔ Go (pgx/GORM) Transaction Patterns
✔ Node.js (pg) Transaction Patterns
✔ Production Best Practices
🔵 EP.46 Middleware & Modular Architecture in Go and Node.js
In the next episode, we will explore how to build backend systems that are clean, maintainable, and production-ready using both Go and Node.js.
- Middleware Fundamentals
- Service / Repository Pattern
- Production-Ready Project Structure