From c2162c192146b404d95a1fc8d1b91356661f7743 Mon Sep 17 00:00:00 2001 From: Chinonso-Peter Date: Mon, 23 Feb 2026 16:16:22 +0100 Subject: [PATCH] Improved Database Optimization and Performance --- docs/DATABASE_SCHEMA.md | 31 +++++++++++++++ prisma/schema.prisma | 57 +++++++++++++++++---------- src/database/prisma/prisma.service.ts | 31 +++++++++++---- 3 files changed, 90 insertions(+), 29 deletions(-) diff --git a/docs/DATABASE_SCHEMA.md b/docs/DATABASE_SCHEMA.md index d1c4b85f..ae2209d2 100644 --- a/docs/DATABASE_SCHEMA.md +++ b/docs/DATABASE_SCHEMA.md @@ -1,3 +1,34 @@ +## Scaling Strategies + +### Vertical Scaling +- Increase CPU, RAM, and disk IOPS on the PostgreSQL server for higher throughput. +- Adjust `connection_limit` in the DATABASE_URL to match available resources. + +### Horizontal Scaling +- Use read replicas for scaling read-heavy workloads. Configure Prisma to use replicas for read queries (future enhancement). +- For global scale, consider managed PostgreSQL services (e.g., AWS RDS, GCP Cloud SQL) with multi-region support. + +### Connection Pool Tuning +- Tune `connection_limit` and `pool_timeout` in the DATABASE_URL for optimal pool size. +- Monitor active connections and adjust pool size as needed. + +### Caching +- Use Redis for caching frequently accessed data and query results. +- Cache count queries and first-page results for large datasets. + +### Partitioning and Sharding (Advanced) +- For very large tables, consider PostgreSQL table partitioning. +- Sharding is not natively supported by Prisma, but can be implemented at the application layer if needed. + +### High Availability & Failover +- Use managed PostgreSQL with automatic failover and backups. +- Regularly test backup and restore procedures (see backup.sh and restore.sh). + +### Monitoring +- Monitor query performance and slow queries using Prisma logs and PostgreSQL tools (pg_stat_statements). +- Set up external monitoring (e.g., Prometheus, Grafana) for database metrics. + +// See SETUP.md for more performance and monitoring tips. # PropChain Database Schema Documentation ## Overview diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5223645c..feb21c7b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,10 +1,14 @@ // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Prisma client generator generator client { provider = "prisma-client-js" } + +// Database connection configuration datasource db { provider = "postgresql" url = env("DATABASE_URL") @@ -12,8 +16,8 @@ datasource db { model User { id String @id @default(cuid()) - email String @unique - walletAddress String? @unique @map("wallet_address") + email String @unique // Unique index for fast user lookup by email + walletAddress String? @unique @map("wallet_address") // Unique index for wallet address role UserRole @default(USER) roleId String? @map("role_id") password String? @@ -27,10 +31,13 @@ model User { roleChanges RoleChangeLog[] documents Document[] - @@index([email]) - @@index([walletAddress]) - @@index([role]) - @@index([createdAt]) + // Indexes for common query patterns + @@index([email]) // For searching users by email + @@index([walletAddress]) // For searching users by wallet address + @@index([role]) // For filtering users by role + @@index([createdAt]) // For sorting/filtering by creation date + // Consider adding an index on isVerified if you often filter by verification status + @@index([isVerified]) @@map("users") } @@ -68,12 +75,14 @@ model Property { latitude Float? longitude Float? - - @@index([latitude, longitude]) - @@index([ownerId]) - @@index([status]) - @@index([createdAt]) - @@index([location]) + // Indexes for geospatial queries and filtering + @@index([latitude, longitude]) // For location-based queries + @@index([ownerId]) // For filtering properties by owner + @@index([status]) // For filtering by property status + @@index([createdAt]) // For sorting/filtering by creation date + @@index([location]) // For searching/filtering by location + // Consider adding an index on price if you often filter/sort by price + @@index([price]) @@map("properties") } @@ -90,7 +99,10 @@ model PropertyValuation { createdAt DateTime @default(now()) property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) - + + // Index for fast lookup by property and date + @@index([propertyId]) + @@index([valuationDate]) @@map("property_valuations") } @@ -117,17 +129,20 @@ model Transaction { platformFee Decimal? disputeReason String? - property Property? @relation(fields: [propertyId], references: [id], onDelete: SetNull) + property Property? @relation(fields: [propertyId], references: [id], onDelete: SetNull) recipient User? @relation("UserTransactions", fields: [toAddress], references: [walletAddress]) documents Document[] - @@index([buyerId]) - @@index([sellerId]) - @@index([fromAddress]) - @@index([toAddress]) - @@index([status]) - @@index([createdAt]) - @@index([propertyId]) + // Indexes for common transaction queries + @@index([buyerId]) // For filtering by buyer + @@index([sellerId]) // For filtering by seller + @@index([fromAddress]) // For filtering by sender + @@index([toAddress]) // For filtering by recipient + @@index([status]) // For filtering by transaction status + @@index([createdAt]) // For sorting/filtering by creation date + @@index([propertyId]) // For filtering by property + // Consider adding an index on txHash if you often search by transaction hash + @@index([txHash]) @@map("transactions") } diff --git a/src/database/prisma/prisma.service.ts b/src/database/prisma/prisma.service.ts index 4187a915..321a37a4 100644 --- a/src/database/prisma/prisma.service.ts +++ b/src/database/prisma/prisma.service.ts @@ -1,3 +1,8 @@ + +// PrismaService manages the PrismaClient lifecycle and database connection pooling. +// Connection pooling is configured via the DATABASE_URL environment variable. +// Example: postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=30 +// See docs/DATABASE_SCHEMA.md for recommended settings. import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; import { PrismaClient, Prisma } from '@prisma/client'; import { ConfigService } from '@nestjs/config'; @@ -5,13 +10,19 @@ import { StructuredLoggerService } from '../../common/logging/logger.service'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + /** + * PrismaService constructor + * - Configures PrismaClient with connection pooling via DATABASE_URL + * - See docs/DATABASE_SCHEMA.md for recommended pooling settings + * - Logs queries, errors, and warnings + */ constructor( private readonly configService: ConfigService, private readonly logger: StructuredLoggerService, ) { + // Get the database URL from environment/config const databaseUrl = configService.get('DATABASE_URL'); - // Connection pooling configuration via URL parameters // Prisma uses the connection URL to configure pooling // Example: postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=30 super({ @@ -30,17 +41,21 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul this.logger.setContext('PrismaService'); } + /** + * Initialize Prisma connection and enable query performance monitoring. + * Logs query durations and parameters for performance analysis. + */ async onModuleInit() { this.logger.log('Connecting to database...'); - if (this.configService.get('NODE_ENV') === 'development') { - (this as any).$on('query', (e: any) => { - this.logger.logDatabase('query', e.duration, { - query: e.query, - params: e.params, - }); + // Enable query performance monitoring in all environments + (this as any).$on('query', (e: any) => { + // Log query duration, SQL, and parameters + this.logger.logDatabase('query', e.duration, { + query: e.query, + params: e.params, }); - } + }); await this.$connect(); this.logger.log('Database connection established');