packages feed

eventium-postgresql-0.1.0: README.md

# Eventium PostgreSQL

PostgreSQL-based event store implementation for production event sourcing systems.

## Overview

`eventium-postgresql` provides a robust, production-ready event store implementation backed by PostgreSQL. It leverages PostgreSQL's ACID guarantees, indexing capabilities, and reliability for persistent event storage with high performance and data integrity.

## Features

- ✅ **ACID Transactions** - Full consistency guarantees
- ✅ **Optimistic Concurrency** - Prevents lost updates with version checks
- ✅ **Efficient Indexing** - Fast event retrieval by stream and position
- ✅ **Global Event Ordering** - Sequence numbers for time-ordered queries
- ✅ **Type-Safe Access** - Uses Persistent library for database operations
- ✅ **Production Ready** - Battle-tested PostgreSQL backend
- ✅ **Multi-Process Support** - Concurrent access from multiple applications

## Database Schema

The implementation creates two main tables:
- **Events Table** - Stores events with aggregate keys, versions, and payloads
- **Global Events Table** - Maintains global ordering with sequence numbers

Indexes ensure fast lookups by:
- Stream key + version
- Global sequence number
- Event types (for projections)

## Installation

Add to your `package.yaml`:

```yaml
dependencies:
  - eventium-core
  - eventium-sql-common
  - eventium-postgresql
  - persistent-postgresql  # PostgreSQL driver
```

## Usage

```haskell
import Eventium.Store.Postgresql
import Database.Persist.Postgresql

main :: IO ()
main = do
  let connStr = "host=localhost dbname=eventstore user=postgres"
  
  withPostgresqlPool connStr 10 $ \pool -> do
    -- Initialize schema
    flip runSqlPool pool $ do
      runMigration migrateAll
      
      -- Create event store
      let store = makePostgresqlEventStore pool
      
      -- Use with command handlers
      result <- applyCommandHandler 
        (eventStoreWriter store)
        (eventStoreReader store)
        commandHandler
        aggregateId
        command
```

## Configuration

### Connection String

```haskell
-- Basic connection
"host=localhost port=5432 dbname=mydb user=myuser password=mypass"

-- With connection pool
withPostgresqlPool connectionString poolSize $ \pool -> ...
```

### Connection Pooling

Recommended settings for production:
```haskell
-- Pool size based on concurrent requests
poolSize = numCores * 2 + effectiveSpindleCount

-- Example: 10 connections for typical web app
withPostgresqlPool connStr 10 $ \pool -> ...
```

## Setup

### Start PostgreSQL with Docker

```bash
# Using docker-compose (provided in project root)
docker-compose up -d postgres

# Or manually
docker run -d \
  --name eventium-postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=eventstore \
  -p 5432:5432 \
  postgres:15
```

### Run Migrations

```haskell
runSqlPool (runMigration migrateAll) pool
```

## Performance

PostgreSQL provides excellent performance characteristics:
- **Writes**: ~1000-5000 events/sec (single connection)
- **Reads**: ~10000-50000 events/sec (with proper indexing)
- **Scalability**: Read replicas for query scaling

See `postgres-event-store-bench/` for benchmarking scripts.

## Best Practices

1. **Use Connection Pooling** - Essential for web applications
2. **Index Strategy** - Default indexes cover common queries
3. **Backup Strategy** - Regular PostgreSQL backups
4. **Monitoring** - Watch connection pool usage and query performance
5. **Read Replicas** - Scale read models with PostgreSQL replication

## Production Considerations

- **High Availability** - Use PostgreSQL replication
- **Backup & Recovery** - Point-in-time recovery with WAL archiving
- **Monitoring** - Track event growth and query performance
- **Connection Limits** - Configure max_connections appropriately

## Example: Complete Setup

```haskell
import Eventium.Store.Postgresql
import Control.Monad.Logger (runStdoutLoggingT)

setupEventStore :: IO ()
setupEventStore = runStdoutLoggingT $ do
  let connStr = "host=localhost dbname=eventstore"
  
  withPostgresqlPool connStr 10 $ \pool -> do
    -- Run migrations
    flip runSqlPool pool $ runMigration migrateAll
    
    -- Event store is ready to use
    liftIO $ putStrLn "Event store initialized"
```

## Documentation

- [Main README](../README.md) - Project overview
- [SQL Common](../eventium-sql-common/) - Shared SQL utilities
- [Design Documentation](../DESIGN.md) - Architecture details

## License

MIT - see [LICENSE.md](LICENSE.md)