An in-memory database modelled on the SAP HANA column store. Stack and specification.

GGS3-DB keeps tables column by column in main memory and speaks the PostgreSQL protocol. Expressions are compiled to .NET code, analytical queries run in parallel on every core, and a commit becomes visible only after fsync.

In development

As of October 2026

The way a query travels

Client

psql · Npgsql

PostgreSQL protocol

SCRAM-SHA-256 · extended query

SQL compiler

parser · expression trees

Column store in RAM

parallel on every core

Redo log

group commit · fsync

At a glance

The key figures of GGS3-DB, backed by the code.

  • PostgreSQL protocol

    psql and Npgsql connect without a driver of their own

    Protocol 3.0

  • Columns in RAM

    Dictionary encoding for text, parallel scans on every core

    Column store

  • Commit after fsync

    Redo log with group commit, restart from snapshot and log

    Durability

How a query runs

From connecting to the answer, for reads and for writes.

  1. Connect

    Every connection gets a thread of its own. Sign-in uses SCRAM-SHA-256, as with PostgreSQL.

  2. Parse

    A hand-written parser turns the SQL into a syntax tree, over the simple or the extended query protocol.

  3. Compile

    The binder checks types and rights and compiles expressions to .NET code through expression trees. Conditions move to the tables, equality joins become hash joins.

  4. Read

    Readers work on an immutable state and never wait. Columns are scanned in blocks, in parallel on every core.

  5. Write

    One transaction at a time appends to the columns. The commit goes into the redo log and becomes visible only after fsync; concurrent commits share one fsync.

  6. Reply

    Rows go back in text or binary format, as with PostgreSQL.

The stack

One .NET application with no packages beyond the base library, in six parts.

  • Protocol

    PostgreSQL protocol 3.0 with simple and extended query, prepared statements and COPY in text, csv and binary.

  • SQL compiler

    Hand-written parser and binder; expressions are compiled to delegates through System.Linq.Expressions.

  • Execution

    Parallel scans in blocks, hash joins, hash-partitioned GROUP BY and a heap for top-N.

  • Column store

    One array per column, dictionary encoding for text, min/max per block and sorted hash runs for keys.

  • Durability

    Redo log with CRC32C frames and group commit, snapshots at checkpoint, recovery from snapshot and log.

  • Platform

    C# on .NET 10 with server GC; as a container on a chiseled image without a shell.

Measured

A comparison with PostgreSQL 18 using pgbench, and a load test with 50 million rows. The figures come from the repository; raw data is not published.

Conditions

  • Docker Desktop on an M5 Max (18 cores, 64 GB), 8 CPUs and 16 GB per server, fsync and synchronous_commit on.
  • 2 million orders and 100,000 customers; pgbench with prepared statements from postgres:18-alpine, in the server's network.
  • PostgreSQL 18 tuned (shared_buffers 4 GB, work_mem 64 MB, parallel workers), VACUUM ANALYZE after loading.
  • Both servers alternately in two rounds; the better value counts.

Caveats

  • Values vary between runs by 10 to 40 %.
  • For top-N the data is in insert order, which favours GGS3-DB; PostgreSQL has no index on created.
  • Figures are averages, not percentiles.
  • With many clients writing at the same time, PostgreSQL is ahead.

Comparison with PostgreSQL 18

Transactions per second, GGS3-DB / PostgreSQL (factor)

Workload1 client16 clients64 clients
WHERE id = $118,379 / 23,943 (0.77×)105,167 / 140,817 (0.75×)113,463 / 119,121 (0.95×)
count(*) WHERE customer_id = $11,128 / 64 (18×)1,495 / 135 (11×)1,474 / 145 (10×)
GROUP BY status111 / 22 (5.0×)107 / 42 (2.6×)109 / 42 (2.6×)
GROUP BY customer_id (100,000 groups)24 / 2.5 (9.8×)32 / 7.6 (4.2×)30 / 7.1 (4.2×)
JOIN + GROUP BY country54 / 11 (4.7×)35 / 14 (2.5×)27 / 12 (2.3×)
ORDER BY created DESC LIMIT 20386 / 22 (17×)1,817 / 44 (41×)1,578 / 44 (36×)
INSERT, one commit per row9,908 / 9,658 (1.03×)45,894 / 44,622 (1.03×)63,340 / 87,495 (0.72×)
UPDATE … WHERE id = $19,868 / 8,676 (1.14×)47,276 / 41,116 (1.15×)78,051 / 83,525 (0.93×)
80 % reads, 20 % updates13,484 / 20,438 (0.66×)71,063 / 80,519 (0.88×)113,299 / 137,679 (0.82×)

Load test with 50 million rows

In process on an M5 Max (18 cores, 64 GB).

COPY binary, 50 M rows
11.3 s (4.4 M rows/s)
GROUP BY over 1000 groups
0.3 s
GROUP BY over a text column
0.3–0.6 s
filtered count
0.05 s
WHERE id = k, first access (builds the key runs)
0.4 s
JOIN with a 1000-row table plus GROUP BY
0.2–0.4 s
ORDER BY … LIMIT 10
0.3–0.4 s
UPDATE of 5 M rows
0.6 s
Restart (1.1 GB snapshot, index rebuilt)
1.7–1.9 s

Columns in memory.

The PostgreSQL protocol, no driver of its own.

Visible only after fsync.

Specification

Storage

Column store
one array per column; writes only append
Text
dictionary encoding, each row stores just an int code
Further compression
none
UPDATE
marks the old row deleted and appends the new version; the checkpoint removes deleted rows
Keys
PRIMARY KEY and UNIQUE; a key lookup goes through sorted hash runs of 16,384 rows each
Min/max per block
per 65,536 rows, for ORDER BY … LIMIT
Secondary indexes
not supported
Data volume
everything in main memory, at most about 2.1 billion rows per table

Data types

Numbers
smallint, integer, bigint, real, double precision, numeric(p,s) up to 28 digits
Text
text, varchar(n), UTF-8
Time
date, timestamp, timestamptz (UTC)
Booleans
boolean
Not supported
uuid, bytea, json, interval, arrays, SERIAL

SQL

Tables
CREATE TABLE with NOT NULL, PRIMARY KEY and UNIQUE; DROP TABLE
Writes
INSERT … VALUES | SELECT, UPDATE, DELETE, COPY … FROM STDIN in text, csv and binary
Queries
WHERE, INNER and LEFT JOIN, GROUP BY, HAVING, DISTINCT, ORDER BY, LIMIT/OFFSET
Aggregates
count, sum, avg, min, max, including count(DISTINCT x)
Expressions
CASE, CAST, IN, BETWEEN, LIKE/ILIKE, coalesce, date_trunc, extract and more functions
Roles and rights
CREATE, ALTER and DROP ROLE, GRANT and REVOKE per table, owners
Not supported
subqueries, CTEs, UNION, window functions, RIGHT/FULL JOIN, ALTER TABLE, views, foreign keys, RETURNING, ON CONFLICT, SAVEPOINT

Transactions

Model
immutable versions (copy-on-write) and one write lock for the whole database
Readers
work on a fixed state and never wait
Writers
one transaction at a time; others wait up to 30 seconds for the lock
Isolation
READ COMMITTED; REPEATABLE READ and SERIALIZABLE as a snapshot, and a write attempt aborts once any commit happened since the snapshot
Locks per table
instead of one lock for the whole databasePlanned

Durability

Redo log
frames with a CRC32C checksum, the whole transaction per commit
Commit
visible only after fsync; concurrent commits share one fsync (group commit)
Checkpoint
automatic from 1 GiB of log or via CHECKPOINT; writes a snapshot without deleted rows
Restart
newest snapshot plus log; an unfinished transaction at the end is dropped
Tested
a log cut at every byte offset recovers exactly the committed state

Protocol and clients

Protocol
PostgreSQL protocol 3.0, reports itself as version 16.0
Queries
simple and extended query, prepared statements, text and binary format
Plan cache
for prepared INSERT, UPDATE and DELETE
Plan cache for SELECT
today SELECT is compiled again on every executionPlanned
COPY
FROM STDIN in text, csv and binary
Tested clients
Npgsql (with Server Compatibility Mode=NoTypeLoading), psql and pgbench
Not supported
COPY TO, cancelling running queries, LISTEN/NOTIFY, the system catalog (\d, DBeaver, pgAdmin)

Security

Sign-in
SCRAM-SHA-256
Rights
roles, memberships, table privileges including PUBLIC
Root password
from an environment variable or a file, such as a Docker secret
Without a password
the server accepts connections over loopback only
Container
chiseled image without a shell, does not run as root
Connection encryption
not supported (no TLS)

Operations

Start
a .NET application, from the command line or with Docker Compose
Settings
port, address, data directory and maximum number of connections
Connections
one thread per connection, up to 100 by default
Health check
built into the Docker image, green once recovery is done
Metrics
none, only messages on stdout and stderr
Backup
no pg_dump and no documented backup procedure

Limits

A good fit
data that fits in main memory and is mostly analysed: filters without an index, GROUP BY, JOIN, top-N
Better with PostgreSQL
many clients writing at once, data larger than main memory, full SQL, TLS, replication, pg_dump

Licence

Licence
Apache 2.0
Dependencies
no packages in the server beyond the .NET base library
Tests
xunit.v3 and Npgsql

Status

Built
column store, SQL subset, transactions, redo log with recovery, roles and rights, PostgreSQL protocol with COPY, Docker image
Tests
58 automated tests in 9 areas, among them persistence, transactions, protocol and rights
In progress
event loops instead of one thread per connection, a hash index for keys and further OLTP optimisations
Planned
plan cache for SELECT, locks per table

Questions about GGS3-DB

Answered briefly; the details are in the specification.

Also on GGS Network

The other pages, in short.

  • Remote desktop

    Stack and specification of the remote desktop built on Moonlight and Sunshine.

  • Team

    Who designs, builds and runs GGS Network.

  • Status

    Whether the website, API and services are reachable right now, measured continuously.