# PostgreSQL TTL Index Extension - User Guide

## Table of Contents
1. [Overview](#overview)
2. [Prerequisites](#prerequisites)
3. [Installation](#installation)
4. [Quick Start](#quick-start)
5. [Usage Examples](#usage-examples)
6. [Background Worker Management](#background-worker-management)
7. [Configuration](#configuration)
8. [Monitoring](#monitoring)
9. [Troubleshooting](#troubleshooting)
10. [Support](#support)

## Overview

The `pg_ttl_index` extension provides automatic Time-To-Live (TTL) functionality for PostgreSQL tables. It automatically cleans expired data based on timestamp columns, either by hard deletion or optional soft deletion.

### Key Features
- ✅ **Automatic data expiration** - Set it and forget it
- ✅ **Background worker** - Runs cleanup at configurable intervals
- ✅ **Batch deletion** - Handles millions of rows efficiently (v2.0+)
- ✅ **Auto-indexing** - Creates index on timestamp column automatically (v2.0+)
- ✅ **Stats tracking** - Monitor rows deleted per table (v2.0+)
- ✅ **Concurrency control** - Advisory locks prevent overlapping runs (v2.0+)
- ✅ **Soft delete mode** - Mark rows with a timestamp column instead of removing them (v3.0+)
- ✅ **Multiple tables support** - Different expiry times per table
- ✅ **Schema-aware operations** - Supports schema-qualified table names (e.g. `app.sessions`)
- ✅ **search_path hardening** - Function execution is protected from search_path hijacking
- ✅ **Production ready** - ACID compliant with SQL injection protection

## Prerequisites

Before installing the extension, ensure you have:

- **Development tools** (make, gcc, postgresql-server-dev)
- **Superuser privileges** for installation
- **Database restart capability** (for shared_preload_libraries)


## Installation

### Option 1: Install via PGXN (Recommended)

```bash
# Install using PGXN (PostgreSQL Extension Network)
pgxn install pg_ttl_index
```

**Note:** PGXN installation requires the `pgxn` client tool. Install it with:
```bash
# Ubuntu/Debian
sudo apt-get install pgxnclient

# macOS
brew install pgxnclient

# Or install via pip
pip install pgxnclient
```


### Step 2: Configure PostgreSQL

Add the extension to your PostgreSQL configuration:

```bash
# Edit postgresql.conf
sudo nano /etc/postgresql/12/main/postgresql.conf
```

Add or modify this line:
```
shared_preload_libraries = 'pg_ttl_index'
```

**Note:** If you already have other extensions in `shared_preload_libraries`, separate them with commas:
```
shared_preload_libraries = 'pg_stat_statements,pg_ttl_index'
```

### Step 3: Restart PostgreSQL

```bash
# Ubuntu/Debian
sudo systemctl restart postgresql

# macOS (Homebrew)
brew services restart postgresql
```

### Step 4: Create the Extension

Connect to your database and create the extension:

```sql
-- Connect to your database
\c your_database_name

-- Create the extension
CREATE EXTENSION pg_ttl_index;

-- Start the background worker (required for automatic cleanup)
SELECT ttl_start_worker();

-- Verify installation
\dx pg_ttl_index
```

## Quick Start

### 1. Start the Background Worker

```sql
-- Start the background worker for automatic cleanup
SELECT ttl_start_worker();
```

### 2. Create a Sample Table

```sql
-- Create a table with a timestamp column
CREATE TABLE user_sessions (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL,
    session_data JSONB,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Insert some test data
INSERT INTO user_sessions (user_id, session_data) VALUES
(1, '{"login_time": "2024-01-01 10:00:00"}'),
(2, '{"login_time": "2024-01-01 11:00:00"}'),
(3, '{"login_time": "2024-01-01 12:00:00"}');
```

### 3. Set Up TTL Index

```sql
-- Data expires after 1 hour (3600 seconds)
-- Optional: specify batch_size for high-load tables (default: 10000)
SELECT ttl_create_index('public.user_sessions', 'created_at', 3600);

-- Or with custom batch size for high-volume tables
SELECT ttl_create_index('public.user_sessions', 'created_at', 3600, 5000);

-- Optional soft delete mode (requires nullable timestamp column, e.g. deleted_at)
SELECT ttl_create_index('public.user_sessions', 'created_at', 3600, 5000, 'deleted_at');
```

### 4. Verify TTL Index

```sql
-- Check active TTL indexes with stats
SELECT * FROM ttl_summary();
```

### 5. Test the Cleanup

```sql
-- Manually trigger cleanup (optional - runs automatically via background worker)
SELECT ttl_runner();
```

## Usage Examples

### Example 1: Session Management

```sql
-- Create sessions table
CREATE TABLE sessions (
    session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id INTEGER NOT NULL,
    data JSONB,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Sessions expire after 24 hours (batch size 5000 for high load)
SELECT ttl_create_index('public.sessions', 'created_at', 86400, 5000);
```

### Example 2: Log Cleanup

```sql
-- Create application logs table
CREATE TABLE app_logs (
    id SERIAL PRIMARY KEY,
    level VARCHAR(10) NOT NULL,
    message TEXT NOT NULL,
    metadata JSONB,
    logged_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Logs expire after 7 days
SELECT ttl_create_index('public.app_logs', 'logged_at', 604800);
```

### Example 3: Cache Management

```sql
-- Create cache table
CREATE TABLE cache_entries (
    cache_key VARCHAR(255) PRIMARY KEY,
    cache_value TEXT NOT NULL,
    expires_at TIMESTAMPTZ NOT NULL
);

-- Cache expires based on expires_at column
SELECT ttl_create_index('public.cache_entries', 'expires_at', 0);
```

### Managing TTL Indexes

```sql
-- List all TTL indexes with stats
SELECT * FROM ttl_summary();

-- Returns:
-- schema_name, table_name, column_name, expire_after_seconds, batch_size,
-- active, last_run, time_since_last_run, rows_deleted_last_run,
-- total_rows_deleted, index_name

-- Update expiry time
SELECT ttl_create_index('public.user_sessions', 'created_at', 7200); -- Change to 2 hours

-- Disable TTL index (temporarily)
UPDATE ttl_index_table
SET active = false
WHERE schema_name = 'public' AND table_name = 'user_sessions' AND column_name = 'created_at';

-- Re-enable TTL index
UPDATE ttl_index_table
SET active = true
WHERE schema_name = 'public' AND table_name = 'user_sessions' AND column_name = 'created_at';

-- Remove TTL index completely (also drops the auto-created index)
SELECT ttl_drop_index('public.user_sessions', 'created_at');
```

### Security Note

For predictable behavior in multi-schema databases, always pass schema-qualified table names to `ttl_create_index()` and `ttl_drop_index()`, for example `app.sessions`.

## Background Worker Management

### Starting the Background Worker

The background worker is **not started automatically**. You must start it manually:

```sql
-- Start the background worker
SELECT ttl_start_worker();

-- Check if worker is running
SELECT * FROM ttl_worker_status();
```

### Important Notes

- **Manual Start Required**: The worker does not start automatically after extension installation
- **Per-Database**: You need to start the worker in each database where you want TTL functionality
- **Restart Required**: If PostgreSQL restarts, you'll need to start the worker again
- **Single Worker**: Only one TTL worker runs per database

## Configuration

### Background Worker Settings

```sql
-- Change cleanup interval (default: 60 seconds)
ALTER SYSTEM SET pg_ttl_index.naptime = 30;
SELECT pg_reload_conf();

-- Disable background worker temporarily
ALTER SYSTEM SET pg_ttl_index.enabled = false;
SELECT pg_reload_conf();

-- Re-enable background worker
ALTER SYSTEM SET pg_ttl_index.enabled = true;
SELECT pg_reload_conf();
```

### View Current Configuration

```sql
-- Check current settings
SELECT name, setting, unit, context
FROM pg_settings
WHERE name LIKE 'pg_ttl_index%';
```

## Monitoring

### Check Background Worker Status

```sql
-- View background worker status
SELECT * FROM ttl_worker_status();
```

### Monitor Cleanup Activity

```sql
-- Check deletion stats and last cleanup times
SELECT
    schema_name,
    table_name,
    rows_deleted_last_run,
    total_rows_deleted,
    last_run,
    time_since_last_run
FROM ttl_summary();
```


## Troubleshooting

### Common Issues

#### 1. Extension Not Loading

**Error:** `ERROR: extension "pg_ttl_index" is not available`

**Solution:**
```bash
# Check if extension files are installed
ls -la /usr/share/postgresql/12/extension/pg_ttl_index*

# Reinstall if missing
pgxn install pg_ttl_index

```

#### 2. Background Worker Not Starting

**Error:** No TTL worker processes visible

**Solution:**
```sql
-- Check if extension is created
\dx pg_ttl_index

-- Check shared_preload_libraries setting
SHOW shared_preload_libraries;

-- Restart PostgreSQL after adding to shared_preload_libraries
```

#### 3. Permission Denied

**Error:** `ERROR: permission denied for table ttl_index_table`

**Solution:**
```sql
-- Grant necessary permissions
GRANT USAGE ON SCHEMA public TO your_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ttl_index_table TO your_user;
```

#### 4. Invalid Column Type

**Error:** `Column must be a date/timestamp type`

**Solution:**
```sql
-- Check column data type
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'your_table';

-- Supported types: timestamp, timestamptz, date
```

### Debug Mode

```sql
-- Enable debug logging
ALTER SYSTEM SET log_min_messages = debug1;
SELECT pg_reload_conf();

-- Check logs for detailed TTL activity
```


## Support

- **GitHub Repository:** https://github.com/ibrahimkarimeddin/postgres-extensions-pg_ttl
- **Issues:** https://github.com/ibrahimkarimeddin/postgres-extensions-pg_ttl/issues
- **Documentation:** This user guide
