PostgreSQL to HeliosDB Lite Migration
HeliosDB Lite takes over workloads from PostgreSQL through the same wire protocol: the schema and data are exported with pg_dump, statements for features Lite handles differently, such as extensions, are removed from the dump, the result is loaded into the Lite server with psql, and the application then connects with its existing PostgreSQL driver.
Problem Being Solved
Core Problem Statement
PostgreSQL's client-server architecture imposes unnecessary network overhead, operational complexity, and cloud infrastructure costs for applications that could run with embedded databases.
Who Suffers Most
ISV Software Vendors (Packaged Applications): Shipping software that requires customers to provision PostgreSQL clusters; customers complain about setup complexity and cloud costs; support burden for database configuration/tuning issues; lost deals due to "too complex to deploy."
HeliosDB Lite Solution
Architecture Overview
┌────────────────────────────────────────────────────────────────┐
│ Application Process │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Application Code (Rust/Python/Node/Go) │ │
│ │ │ │
│ │ PostgreSQL Client Library (libpq, node-postgres, etc.) │ │
│ └────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ │ postgresql://localhost/db │
│ │ (connection string) │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ HeliosDB-Lite Wire Adapter │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ PostgreSQL Wire Protocol Handler │ │ │
│ │ │ - Startup message parsing │ │ │
│ │ │ - Query protocol (Simple/Extended) │ │ │
│ │ │ - Data type encoding (text/binary) │ │ │
│ │ │ - COPY protocol │ │ │
│ │ │ - Authentication (trust/password) │ │ │
│ │ └────────────────────┬───────────────────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ SQL Dialect Translator │ │ │
│ │ │ - PostgreSQL → SQLite SQL conversion │ │ │
│ │ │ - Function mapping (NOW() → datetime('now')) │ │ │
│ │ │ - Type mapping (SERIAL → INTEGER PRIMARY KEY) │ │ │
│ │ │ - RETURNING clause emulation │ │ │
│ │ └────────────────────┬───────────────────────────────┘ │ │
│ │ ▼ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ HeliosDB-Lite Core Engine │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ SQL Query Engine │ │ │
│ │ │ - Query parser & optimizer │ │ │
│ │ │ - Execution engine │ │ │
│ │ │ - Index management │ │ │
│ │ └────────────────────┬───────────────────────────────┘ │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ Storage Layer (SQLite-compatible) │ │ │
│ │ │ - B-tree indexes │ │ │
│ │ │ - WAL (Write-Ahead Logging) │ │ │
│ │ │ - ACID transactions │ │ │
│ │ │ - Page cache (configurable) │ │ │
│ │ └────────────────────┬───────────────────────────────┘ │ │
│ │ ▼ │ │
│ │ app_data.db │ │
│ │ (Single file on disk) │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────┘
Single Process
Zero Network Overhead
No Separate Database Server Required
Concrete Examples with Code, Config & Architecture
Example 1: Embedded Configuration for PostgreSQL Migration
HeliosDB Lite Configuration (helios_postgres_compat.toml):
[database]
type = "embedded"
path = "./app_data.db"
mode = "readwrite-create"
page_size = 8192 # Match PostgreSQL page size
cache_size_mb = 512
wal_mode = true
sync_mode = "full" # ACID compliance
journal_mode = "wal"
[postgres_compatibility]
enabled = true
listen_address = "127.0.0.1"
listen_port = 5432
max_connections = 100
authentication_mode = "trust" # or "md5", "scram-sha-256"
# SQL dialect translation
[dialect]
target_dialect = "postgresql"
translate_functions = true
translate_types = true
emulate_sequences = true
support_returning = true
support_cte = true
support_window_functions = true
# Type mappings
[dialect.type_mappings]
"SERIAL" = "INTEGER PRIMARY KEY AUTOINCREMENT"
"BIGSERIAL" = "INTEGER PRIMARY KEY AUTOINCREMENT"
"TEXT" = "TEXT"
"VARCHAR" = "TEXT"
"TIMESTAMP" = "TEXT" # ISO8601 format
"TIMESTAMPTZ" = "TEXT"
"JSONB" = "TEXT" # JSON functions supported
"UUID" = "TEXT"
"INET" = "TEXT"
"BOOLEAN" = "INTEGER" # 0/1
# Function mappings
[dialect.function_mappings]
"NOW()" = "datetime('now')"
"CURRENT_TIMESTAMP" = "datetime('now')"
"CURRENT_DATE" = "date('now')"
"RANDOM()" = "random()"
"COALESCE" = "COALESCE" # Passthrough
"NULLIF" = "NULLIF"
"GREATEST" = "MAX"
"LEAST" = "MIN"
# pg_dump import settings
[import]
buffer_size_mb = 128
use_transactions = true
skip_on_error = false
log_file = "./import.log"
[performance]
optimize_queries = true
auto_vacuum = true
vacuum_interval_hours = 24
analyze_on_import = true
[monitoring]
log_queries = false
log_slow_queries_ms = 1000
export_metrics = true
metrics_port = 9091
Migration Script (migrate_from_postgres.sh):
#!/bin/bash
set -e
echo " PostgreSQL to HeliosDB-Lite Migration"
echo "========================================="
# Configuration
POSTGRES_HOST="myapp-db.xxx.rds.amazonaws.com"
POSTGRES_PORT="5432"
POSTGRES_DB="production"
POSTGRES_USER="admin"
DUMP_FILE="./postgres_dump.sql"
HELIOS_CONFIG="./helios_postgres_compat.toml"
HELIOS_DB="./app_data.db"
echo ""
echo "Step 1: Export PostgreSQL database using pg_dump"
echo "------------------------------------------------"
pg_dump -h "$POSTGRES_HOST" \
-p "$POSTGRES_PORT" \
-U "$POSTGRES_USER" \
-d "$POSTGRES_DB" \
--format=plain \
--no-owner \
--no-privileges \
--clean \
--if-exists \
> "$DUMP_FILE"
echo " Dump created: $(wc -l < $DUMP_FILE) lines, $(du -h $DUMP_FILE | cut -f1)"
echo ""
echo "Step 2: Import into HeliosDB-Lite"
echo "----------------------------------"
heliosdb-lite import \
--config "$HELIOS_CONFIG" \
--input "$DUMP_FILE" \
--database "$HELIOS_DB" \
--format pg_dump \
--verbose
echo " Import complete"
echo ""
echo "Step 3: Verify migration"
echo "------------------------"
heliosdb-lite query \
--database "$HELIOS_DB" \
--sql "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"
echo ""
echo "Step 4: Analyze & optimize"
echo "--------------------------"
heliosdb-lite exec \
--database "$HELIOS_DB" \
--command "VACUUM; ANALYZE;"
echo " Database optimized"
echo ""
echo "Step 5: Test query compatibility"
echo "--------------------------------"
# Run sample queries to test translation
heliosdb-lite query \
--database "$HELIOS_DB" \
--sql "SELECT COUNT(*) as total_users FROM users;"
heliosdb-lite query \
--database "$HELIOS_DB" \
--sql "SELECT NOW() as current_time;"
echo ""
echo " Migration complete!"
echo ""
echo "Next steps:"
echo "1. Update application connection string to: postgresql://localhost:5432/$POSTGRES_DB"
echo "2. Start HeliosDB-Lite wire adapter: heliosdb-lite serve --config $HELIOS_CONFIG"
echo "3. Test application thoroughly"
Example 2: Python Application Migration (Django/SQLAlchemy)
Django Migration (settings.py):
# BEFORE: PostgreSQL on RDS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'production',
'USER': 'admin',
'PASSWORD': 'secret',
'HOST': 'myapp-db.xxx.rds.amazonaws.com',
'PORT': '5432',
'CONN_MAX_AGE': 600,
'OPTIONS': {
'connect_timeout': 10,
'sslmode': 'require',
}
}
}
# AFTER: HeliosDB-Lite with PostgreSQL wire protocol
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'production',
'USER': 'app',
'PASSWORD': '', # Trust authentication
'HOST': '127.0.0.1',
'PORT': '5432',
'CONN_MAX_AGE': 0, # Embedded DB doesn't need pooling
'OPTIONS': {
'connect_timeout': 1,
}
}
}
# That's it! Zero model changes needed.
Architecture Diagram:
┌──────────────────────────────────────────────────────────┐
│ Python Application │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Django / Flask / FastAPI │ │
│ │ - Models (ORM) │ │
│ │ - Views/Routes │ │
│ │ - Business Logic │ │
│ └─────────────────┬──────────────────────────────────┘ │
│ │ │
│ │ psycopg2 / asyncpg │
│ │ (PostgreSQL driver) │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ HeliosDB-Lite PostgreSQL Wire Adapter │ │
│ │ - Protocol handler (localhost:5432) │ │
│ │ - SQL dialect translator │ │
│ │ - Type conversions │ │
│ └─────────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ HeliosDB-Lite Core (Embedded SQLite) │ │
│ │ - File: production.db │ │
│ │ - ACID transactions │ │
│ │ - Full-text search │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
Example 3: Docker & Kubernetes Deployment
Dockerfile (Before - Multi-Container):
# BEFORE: Separate PostgreSQL container
version: '3.8'
services:
app:
build: .
depends_on:
- db
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/production
ports:
- "8080:8080"
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=production
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
pgdata:
Dockerfile (After - Single Container with HeliosDB Lite):
FROM rust:1.75-slim as builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy application binary
COPY --from=builder /app/target/release/myapp /usr/local/bin/
# Copy HeliosDB-Lite configuration
COPY helios_postgres_compat.toml /app/config/
# Create data directory
RUN mkdir -p /app/data
# Single process runs both app + embedded DB
ENV DATABASE_URL=postgresql://localhost:5432/production
ENV HELIOS_CONFIG=/app/config/helios_postgres_compat.toml
ENV HELIOS_DB_PATH=/app/data/production.db
EXPOSE 8080
CMD ["myapp"]
docker-compose.yml (Simplified):
version: '3.8'
services:
app:
build: .
container_name: myapp
ports:
- "8080:8080"
- "9091:9091" # Metrics
volumes:
- app-data:/app/data
- ./config:/app/config:ro
environment:
- RUST_LOG=info
- HELIOS_DB_PATH=/app/data/production.db
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
volumes:
app-data:
driver: local
Kubernetes Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:latest
ports:
- containerPort: 8080
name: http
- containerPort: 9091
name: metrics
env:
- name: HELIOS_DB_PATH
value: "/data/production.db"
- name: DATABASE_URL
value: "postgresql://localhost:5432/production"
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /app/config
readOnly: true
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: data
persistentVolumeClaim:
claimName: myapp-data-pvc
- name: config
configMap:
name: helios-config
---
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: production
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: myapp
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-data-pvc
namespace: production
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
Example 4: Node.js Migration with Connection Pooling
Node.js Application (Before):
const { Pool } = require('pg');
// BEFORE: RDS PostgreSQL with connection pooling
const pool = new Pool({
host: 'myapp-db.xxx.rds.amazonaws.com',
port: 5432,
database: 'production',
user: 'admin',
password: process.env.DB_PASSWORD,
max: 20, // Connection pool size
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
ssl: { rejectUnauthorized: false }
});
async function getUsers() {
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users LIMIT 10');
return result.rows;
} finally {
client.release();
}
}
async function createUser(name, email) {
const client = await pool.connect();
try {
await client.query('BEGIN');
const result = await client.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id',
[name, email]
);
await client.query('COMMIT');
return result.rows[0].id;
} catch (e) {
await client.query('ROLLBACK');
throw e;
} finally {
client.release();
}
}
module.exports = { getUsers, createUser };
Node.js Application (After - HeliosDB Lite):
const { Pool } = require('pg');
// AFTER: HeliosDB-Lite via localhost
const pool = new Pool({
host: '127.0.0.1',
port: 5432,
database: 'production',
user: 'app',
max: 1, // Embedded DB doesn't need pooling
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 500, // Local connection is fast
// No SSL needed for localhost
});
// ZERO CHANGES TO APPLICATION CODE BELOW
async function getUsers() {
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users LIMIT 10');
return result.rows;
} finally {
client.release();
}
}
async function createUser(name, email) {
const client = await pool.connect();
try {
await client.query('BEGIN');
const result = await client.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id',
[name, email]
);
await client.query('COMMIT');
return result.rows[0].id;
} catch (e) {
await client.query('ROLLBACK');
throw e;
} finally {
client.release();
}
}
// Performance test
async function benchmarkQueries() {
console.log('Running benchmark...\n');
// Test 1: Simple SELECT
const start1 = Date.now();
for (let i = 0; i < 1000; i++) {
await pool.query('SELECT 1');
}
const latency1 = Date.now() - start1;
console.log(` 1000 SELECT queries: ${latency1}ms (${latency1/1000}ms avg)`);
// Test 2: INSERT with RETURNING
const start2 = Date.now();
for (let i = 0; i < 100; i++) {
await pool.query(
'INSERT INTO test_table (value) VALUES ($1) RETURNING id',
[i]
);
}
const latency2 = Date.now() - start2;
console.log(` 100 INSERT queries: ${latency2}ms (${latency2/100}ms avg)`);
// Test 3: Transaction
const start3 = Date.now();
const client = await pool.connect();
try {
await client.query('BEGIN');
for (let i = 0; i < 50; i++) {
await client.query('INSERT INTO test_table (value) VALUES ($1)', [i]);
}
await client.query('COMMIT');
} finally {
client.release();
}
const latency3 = Date.now() - start3;
console.log(` 50-statement transaction: ${latency3}ms`);
}
module.exports = { getUsers, createUser, benchmarkQueries };
Example 5: Go Microservice Migration
Go Application with PostgreSQL:
package main
import (
"context"
"database/sql"
"fmt"
"log"
"time"
_ "github.com/lib/pq"
)
type User struct {
ID int
Name string
Email string
CreatedAt time.Time
}
func main() {
// BEFORE: RDS PostgreSQL
// connStr := "postgresql://admin:secret@myapp-db.xxx.rds.amazonaws.com:5432/production?sslmode=require"
// AFTER: HeliosDB-Lite
connStr := "postgresql://app@localhost:5432/production?sslmode=disable"
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Configure connection pool (not needed for embedded, but harmless)
db.SetMaxOpenConns(5)
db.SetMaxIdleConns(2)
db.SetConnMaxLifetime(time.Hour)
// Test connection
ctx := context.Background()
if err := db.PingContext(ctx); err != nil {
log.Fatal("Cannot connect:", err)
}
fmt.Println(" Connected to database")
// Test PostgreSQL features
testPostgreSQLFeatures(ctx, db)
}
func testPostgreSQLFeatures(ctx context.Context, db *sql.DB) {
fmt.Println("\nTesting PostgreSQL compatibility...\n")
// 1. CREATE TABLE with SERIAL
_, err := db.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
)
`)
if err != nil {
log.Fatal("CREATE TABLE failed:", err)
}
fmt.Println(" CREATE TABLE with SERIAL type")
// 2. INSERT with RETURNING
var userID int
err = db.QueryRowContext(ctx,
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id",
"David", "david@example.com",
).Scan(&userID)
if err != nil {
log.Fatal("INSERT RETURNING failed:", err)
}
fmt.Printf(" INSERT RETURNING: User ID = %d\n", userID)
// 3. NOW() function
var currentTime time.Time
err = db.QueryRowContext(ctx, "SELECT NOW()").Scan(¤tTime)
if err != nil {
log.Fatal("NOW() failed:", err)
}
fmt.Printf(" NOW() function: %s\n", currentTime.Format(time.RFC3339))
// 4. CTE (Common Table Expression)
rows, err := db.QueryContext(ctx, `
WITH recent_users AS (
SELECT id, name, email FROM users
ORDER BY id DESC LIMIT 5
)
SELECT * FROM recent_users
`)
if err != nil {
log.Fatal("CTE query failed:", err)
}
defer rows.Close()
count := 0
for rows.Next() {
var user User
if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
log.Fatal(err)
}
count++
}
fmt.Printf(" CTE query: %d users\n", count)
// 5. Transaction with savepoints
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Fatal("BEGIN failed:", err)
}
_, err = tx.ExecContext(ctx,
"INSERT INTO users (name, email) VALUES ($1, $2)",
"Emma", "emma@example.com",
)
if err != nil {
tx.Rollback()
log.Fatal("Transaction failed:", err)
}
if err = tx.Commit(); err != nil {
log.Fatal("COMMIT failed:", err)
}
fmt.Println(" Transaction committed")
// 6. Prepared statement
stmt, err := db.PrepareContext(ctx,
"SELECT id, name, email FROM users WHERE email = $1",
)
if err != nil {
log.Fatal("Prepare failed:", err)
}
defer stmt.Close()
var user User
err = stmt.QueryRowContext(ctx, "david@example.com").Scan(
&user.ID, &user.Name, &user.Email,
)
if err != nil {
log.Fatal("Prepared query failed:", err)
}
fmt.Printf(" Prepared statement: Found user %s\n", user.Name)
fmt.Println("\nAll checks passed.")
}
func benchmarkQueries(ctx context.Context, db *sql.DB) {
fmt.Println("\nRunning benchmark...\n")
// Benchmark: 1000 simple queries
start := time.Now()
for i := 0; i < 1000; i++ {
var result int
db.QueryRowContext(ctx, "SELECT 1").Scan(&result)
}
elapsed := time.Since(start)
fmt.Printf(" 1000 queries: %v (%.2fms avg)\n",
elapsed, float64(elapsed.Milliseconds())/1000.0)
// Benchmark: Bulk insert
start = time.Now()
tx, _ := db.BeginTx(ctx, nil)
for i := 0; i < 1000; i++ {
tx.ExecContext(ctx,
"INSERT INTO test_data (value) VALUES ($1)", i,
)
}
tx.Commit()
elapsed = time.Since(start)
fmt.Printf(" 1000 inserts in transaction: %v\n", elapsed)
}
Architecture Diagram:
┌────────────────────────────────────────────────────────┐
│ Go Microservice │
│ ┌──────────────────────────────────────────────────┐ │
│ │ HTTP Server (Gin/Echo/Chi) │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ Business Logic │ │ │
│ │ │ - User service │ │ │
│ │ │ - Order service │ │ │
│ │ │ - Payment service │ │ │
│ │ └────────────────┬───────────────────────────┘ │ │
│ │ │ │ │
│ │ │ lib/pq (PostgreSQL driver) │ │
│ │ ▼ │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ database/sql │ │ │
│ │ │ - Connection pooling │ │ │
│ │ │ - Prepared statements │ │ │
│ │ └────────────────┬───────────────────────────┘ │ │
│ └───────────────────┼───────────────────────────────┘ │
│ │ TCP localhost:5432 │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ HeliosDB-Lite Wire Adapter │ │
│ │ - PostgreSQL protocol │ │
│ │ - SQL translation │ │
│ └──────────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ HeliosDB-Lite Embedded (production.db) │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
Adoption Strategy
Phase 1: Assessment & Planning (Week 1)
- Inventory PostgreSQL Usage: List all databases, sizes, query patterns
- Estimate Savings: Calculate current RDS costs vs. embedded approach
- Select Pilot Service: Choose non-critical microservice with low risk
Phase 2: Pilot Migration (Weeks 2-3)
- Export Database:
pg_dump -Fc production > dump.sql - Import to HeliosDB Lite:
heliosdb-lite import --format pg_dump dump.sql - Update Connection String: Change
postgresql://rds-host:5432/dbtopostgresql://localhost:5432/db - Deploy & Test: Run integration tests; compare query results
- Load Test: Verify performance under production traffic patterns
- Monitor: Track latency, errors, resource usage for 1 week
Phase 3: Rollout (Weeks 4-12)
- Tune Performance: Optimize page cache, vacuum settings
- Update Documentation: Deployment guides, runbooks
- Train Team: Best practices for embedded databases
Conclusion
HeliosDB Lite bridges this gap by providing wire protocol compatibility with PostgreSQL while delivering the operational simplicity and performance of embedded databases.
Performance depends on workload and hardware. Published figures are measured on representative fixtures; see the benchmarks page and reproduce them on your own workload.