This project demonstrates a Two-Phase Commit (2PC) protocol implementation using Node.js and PostgreSQL. The system consists of three Node.js applications:
- Coordinator: Orchestrates the 2PC protocol between participant services
- User Service: Manages user data and participates in the 2PC protocol
- Order Service: Manages order data and participates in the 2PC protocol
The system follows a distributed transaction architecture using the Two-Phase Commit protocol:
- Phase 1 (Prepare): The coordinator asks all participants to prepare for a transaction. Each participant validates the request, executes the transaction without committing, and responds with readiness.
- Phase 2 (Commit/Rollback): If all participants are ready, the coordinator instructs them to commit. If any participant fails, all are instructed to rollback.
PostgreSQL's native support for 2PC is used through the PREPARE TRANSACTION, COMMIT PREPARED, and ROLLBACK PREPARED commands. The PostgreSQL configuration includes setting max_prepared_transactions=100 to enable support for prepared transactions, which is required for the 2PC protocol.
- Docker and Docker Compose
The project includes a convenience script run.sh that:
- Installs all dependencies for the coordinator and services
- Starts the PostgreSQL containers using Docker Compose
- Starts all three Node.js applications locally in separate terminals
- Launches a sample transaction
To use the script:
# Make the script executable (if not already)
chmod +x run.sh
# Run the script
./run.sh- Clone the repository
- Start the PostgreSQL containers using Docker Compose:
# If containers were already running, stop and remove them first
docker-compose down
# Start the containers with the updated configuration
docker-compose up -dNote: If you've previously run the system, make sure to restart the PostgreSQL containers to apply the configuration for prepared transactions.
- Install dependencies for each service:
npm install
cd coordinator && npm install && cd ..
cd service1 && npm install && cd ..
cd service2 && npm install && cd ..- Start each service in a separate terminal:
# Terminal 1
cd coordinator && node src/index.js
# Terminal 2
cd service1 && node src/index.js
# Terminal 3
cd service2 && node src/index.jsThis setup will:
- Start two PostgreSQL databases (for User Service and Order Service) in Docker containers
- Run the three Node.js services (Coordinator, User Service, and Order Service) locally
The project uses environment variables for configuration, which are stored in .env files:
PORT=3000
SERVICE1_URL=http://localhost:3001
SERVICE2_URL=http://localhost:3002
PORT=3001
PGHOST=localhost
PGUSER=postgres
PGPASSWORD=postgres
PGDATABASE=user_service_db
PGPORT=5432
PORT=3002
PGHOST=localhost
PGUSER=postgres
PGPASSWORD=postgres
PGDATABASE=order_service_db
PGPORT=5433
The project includes a transactions.http file at the root level that contains example requests for testing:
- Successful Transaction
- Unsuccessful Transaction (with negative quantity)
- Get All Transactions
- Get Specific Transaction
If you're using VS Code with the REST Client extension or JetBrains IDEs, you can execute these requests directly from the file.
You can also test the system by initiating a transaction through the coordinator using curl:
curl -X POST http://localhost:3000/transaction \
-H "Content-Type: application/json" \
-d '{
"user": {
"username": "john_doe",
"email": "john@example.com"
},
"order": {
"user_id": 1,
"product_name": "Smartphone",
"quantity": 1,
"total_price": 999.99
}
}'curl -X POST http://localhost:3000/transaction \
-H "Content-Type: application/json" \
-d '{
"user": {
"username": "jane_doe",
"email": "jane@example.com"
},
"order": {
"user_id": 2,
"product_name": "Laptop",
"quantity": -1,
"total_price": 1499.99
}
}'curl http://localhost:3000/transaction/{transactionId}Replace {transactionId} with the ID returned from the transaction creation request.
POST /transaction: Initiates a new transactionGET /transaction/:id: Gets the status of a specific transactionGET /transactions: Lists all transactions
POST /prepare: Prepares a transaction (called by coordinator)POST /commit: Commits a prepared transaction (called by coordinator)POST /rollback: Rolls back a prepared transaction (called by coordinator)GET /transaction/:id: Gets the status of a specific transaction
POST /prepare: Prepares a transaction (called by coordinator)POST /commit: Commits a prepared transaction (called by coordinator)POST /rollback: Rolls back a prepared transaction (called by coordinator)GET /transaction/:id: Gets the status of a specific transaction
The system leverages PostgreSQL's built-in support for two-phase commit:
BEGIN: Starts a transactionPREPARE TRANSACTION 'name': Prepares the transaction for commitCOMMIT PREPARED 'name': Commits a prepared transactionROLLBACK PREPARED 'name': Rolls back a prepared transaction
The system handles various error scenarios:
- Prepare phase failures
- Commit phase failures
- Rollback failures
- Network issues between services
In case of failures, the system attempts to maintain consistency by rolling back all prepared transactions.