Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions NEON-CONNECTION-SOLUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Neon Database Connection Solution

We've successfully solved the issue with Neon database connections timing out due to the auto-suspend feature. Here's a summary of what we've accomplished and how to use the solution going forward.

## The Problem

Neon's auto-suspend feature (also called "scale to zero") automatically suspends database computes after a period of inactivity (5 minutes on the free tier). When an application tries to connect to a suspended database, there's a delay as the database wakes up, which can cause connection timeouts.

## Our Solution

We've created several utilities to handle this situation:

1. **Connection Pool with Retry Logic** (`src/lib/db-connection.js`)
- Creates a PostgreSQL connection pool that can handle reconnection
- Automatically retries failed connections with exponential backoff
- Gracefully handles SSL configuration issues

2. **Prisma Client with Retry Logic** (`src/lib/prisma-with-retry.js`)
- Wraps the Prisma client with robust error handling
- Automatically retries queries that fail due to connection issues
- Maintains singleton connection for efficiency

3. **Setup Script** (`setup-neon-connection.js`)
- Interactive tool to configure your database connection
- Tests connection and configures proper SSL settings
- Creates or updates `.env` file with correct settings

4. **Configuration Guide** (`NEON-DATABASE-GUIDE.md`)
- Comprehensive guide on handling Neon's auto-suspend feature
- Alternative database options if you prefer not to use Neon
- Migration steps if you choose to switch databases

## How to Use the Solution

### 1. For Direct Database Access

Replace direct database access code with our connection pool:

```javascript
// Before
const { Client } = require('pg');
const client = new Client({ connectionString: process.env.DATABASE_URL });
await client.connect();
const result = await client.query('SELECT * FROM users');

// After
const { executeQuery } = require('./src/lib/db-connection');
const result = await executeQuery('SELECT * FROM users');
```

### 2. For Prisma Usage

Replace Prisma imports to use our enhanced version:

```javascript
// Before
import { db } from '@/lib/db';
const users = await db.user.findMany();

// After
import prisma from '@/lib/prisma-with-retry';
const users = await prisma.user.findMany();
```

### 3. Environment Configuration

In your `.env` file, you should now have these settings:

```
DATABASE_URL='your-connection-string'
DATABASE_SSL='true/false'
PG_POOL_MAX=20
PG_CONNECTION_RETRIES=3
```

## Alternatives to Handling Neon Auto-Suspend

If you prefer not to deal with auto-suspend, you have these options:

1. **Disable Auto-Suspend in Neon Dashboard**
- Go to Neon Console → Projects → Branches → Compute → Edit
- Uncheck "Scale to zero"
- Note: This may increase costs on paid plans

2. **Use a Different Database Provider**
- Supabase, Railway, or a local PostgreSQL installation
- See `NEON-DATABASE-GUIDE.md` for detailed migration steps

## Next Steps

1. **Test Your Application**: Run your application to ensure it handles database connections properly.

2. **Monitor Connection Behavior**: Watch your logs for any connection issues.

3. **Consider Database Options**: Decide if Neon's auto-suspend works for your use case, or if you should:
- Disable auto-suspend for production
- Keep auto-suspend but use our connection handling
- Switch to a different database provider

## Conclusion

Your application should now be much more resilient to Neon's auto-suspend feature. The connection pool and retry logic will automatically handle reconnection when your database wakes up from suspension, making your application more robust.

For any questions or issues, refer to the detailed guide in `NEON-DATABASE-GUIDE.md`.
130 changes: 130 additions & 0 deletions NEON-DATABASE-GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Fixing Neon Database Auto-Suspend Issues

This guide provides multiple approaches to handle Neon's database auto-suspend feature and offers alternatives if you prefer to switch to a different database provider.

## Understanding Neon's Auto-Suspend Feature

Neon automatically suspends databases after a period of inactivity (5 minutes on free tier) to save resources. While this helps reduce costs, it can cause connection issues in applications that aren't designed to handle these interruptions.

## Option 1: Improve Your Application's Resilience to Auto-Suspend

We've created several tools in this repository to help your application handle Neon's auto-suspend feature:

1. **Connection Pool with Retry Logic**: Use `src/lib/db-connection.js` to create a connection pool that automatically retries connections when the database wakes up.

2. **Prisma Client with Retry Logic**: Use `src/lib/prisma-with-retry.js` for a Prisma-specific implementation that handles reconnection.

3. **Setup Script**: Run `node setup-neon-connection.js` to configure your environment variables and test your connection.

### How to Implement:

1. Replace your direct Prisma usage with our enhanced version:

```javascript
// Before:
import { db } from '@/lib/db'

// After:
import prisma from '@/lib/prisma-with-retry'
```

2. Update your code to use the enhanced client:

```javascript
// Before:
const users = await db.user.findMany();

// After:
const users = await prisma.user.findMany();
```

## Option 2: Disable Auto-Suspend in Neon Dashboard

For production applications, you may want to disable auto-suspend completely:

1. Go to https://console.neon.tech/app/projects/
2. Select your project
3. Click on "Branches" and select your branch
4. Go to the "Computes" tab
5. Click "Edit" on your compute
6. Uncheck "Scale to zero" to keep your compute always running
7. Save your changes

Note: This option will use more compute time, which may increase costs on paid plans.

## Option 3: Switch to a Different Database Provider

If Neon's auto-suspend feature doesn't work for your use case, consider these alternatives:

### 1. Supabase PostgreSQL

Supabase provides a PostgreSQL database that doesn't suspend on their paid plans.

**Setup Steps:**
1. Create an account at https://supabase.com/
2. Create a new project
3. Get your connection string from the "Settings" > "Database" section
4. Update your application's DATABASE_URL to use the Supabase connection string

**Pros:**
- Stable, always-on connection
- Additional features like auth, storage, and realtime subscriptions
- Compatible with Prisma

### 2. Railway PostgreSQL

Railway offers PostgreSQL databases with simple deployment and pricing.

**Setup Steps:**
1. Create an account at https://railway.app/
2. Create a new PostgreSQL database
3. Get your connection string from the "Connect" tab
4. Update your application's DATABASE_URL to use the Railway connection string

**Pros:**
- Simple pricing
- Always-on connection
- Easy to set up

### 3. Local PostgreSQL (for Development)

For development purposes, you can run PostgreSQL locally.

**Setup Steps:**
1. Install PostgreSQL from https://www.postgresql.org/download/
2. Create a database for your project
3. Update your application's DATABASE_URL to use localhost:
```
DATABASE_URL=postgresql://postgres:password@localhost:5432/jackerbox
```

**Pros:**
- No internet connection needed
- No suspension issues
- Fast local development

## Migrating from Neon to Another Database

If you decide to switch databases:

1. Export your data from Neon:
```bash
pg_dump -h ep-fancy-wind-a5ymnajb.us-east-2.aws.neon.tech -U jackerboxDB_owner -d jackerboxDB -f jackerbox_backup.sql
```

2. Import to your new database:
```bash
psql -h your_new_host -U your_new_user -d your_new_database -f jackerbox_backup.sql
```

3. Update your application's DATABASE_URL to point to the new database

## Need More Help?

Run our interactive setup script to configure your application:

```bash
node setup-neon-connection.js
```

This script will walk you through setting up your database connection with proper configuration for handling Neon's auto-suspend feature.
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,53 @@ The S3 service provides the following functions:
- `copyObject(sourceKey, destinationKey)` - Copy an object within S3
- `deleteFromS3(key)` - Delete an object from S3
- `invalidateCloudFrontCache(paths)` - Invalidate CloudFront cache for specific paths

## Payment System

### Overview

The application includes a complete payment system built on Stripe. Key features include:

- Processing payments with Stripe PaymentIntent
- Handling success, failure, and refund scenarios
- Security deposit management
- Webhook processing for real-time payment updates

### Payment Service

The `PaymentService` class (`src/lib/services/payment.ts`) provides the core functionality:

- `createPaymentIntent` - Creates a new payment intent with Stripe
- `handlePaymentSuccess` - Updates payment and rental records when payment succeeds
- `handlePaymentFailure` - Handles failed payments
- `refundPayment` - Processes refunds
- `blockPayment` - Blocks problematic payments
- `scheduleRetry` - Schedules payment retries

### Webhook Handling

The system includes a robust webhook handling system:

- Production endpoint: `/api/webhooks/stripe`
- Development testing endpoint: `/api/webhooks/stripe/dev`
- Shared webhook handler logic in `src/lib/webhooks/stripe-webhook-handler.ts`

### Testing

To test the payment system:

1. Visit `/admin/payment-test` (admin only) to create and process test payments
2. Use the Stripe CLI to send test webhook events to the dev endpoint
3. Run the test suite with `npm test` to verify payment functionality

### Stripe Configuration

Required environment variables:

```
STRIPE_SECRET_KEY=sk_test_your_key
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_your_key
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret
```

For production, use live keys instead of test keys.
11 changes: 11 additions & 0 deletions check-data.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
\echo 'Checking Users:'
SELECT * FROM "User";

\echo 'Checking Equipment:'
SELECT * FROM "Equipment";

\echo 'Checking Rentals:'
SELECT * FROM "Rental";

\echo 'Checking Messages:'
SELECT * FROM "Message";
35 changes: 35 additions & 0 deletions db-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const { executeQuery, testConnection, closePool } = require('./src/lib/db-connection');

async function runTest() {
console.log('Running database connection test...');

try {
// First test the connection
await testConnection();

// Run a few simple queries
console.log('\nRunning sample queries:');

// Get database version
const versionResult = await executeQuery('SELECT version()');
console.log('Database version:', versionResult.rows[0].version);

// Get current timestamp
const timeResult = await executeQuery('SELECT NOW() as current_time');
console.log('Current database time:', timeResult.rows[0].current_time);

// Try a query with parameters
const paramResult = await executeQuery('SELECT $1::text as message', ['Connection pool is working!']);
console.log('Message:', paramResult.rows[0].message);

console.log('\nAll queries completed successfully!');
} catch (error) {
console.error('Error during testing:', error);
} finally {
// Always close the pool when done
await closePool();
}
}

// Run the test
runTest();
20 changes: 20 additions & 0 deletions db.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
$env:PGPASSWORD="monkey2123"
$psqlPath = "C:\Program Files\PostgreSQL\16\bin\psql.exe"

function Invoke-Psql {
param(
[Parameter(Position=0, Mandatory=$false)]
[string]$command = "",
[Parameter(Position=1, Mandatory=$false)]
[string]$database = "jackerbox"
)

if ($command -eq "") {
& $psqlPath -U postgres -d $database
} else {
& $psqlPath -U postgres -d $database -c $command
}
}

# Export the function so it can be used from the command line
Export-ModuleMember -Function Invoke-Psql
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: '3.8'
services:
redis:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
- redis_data:/data
command: redis-server --requirepass ${REDIS_PASSWORD:-defaultpassword}
restart: unless-stopped

volumes:
redis_data:
Loading