Neon’s architecture is designed to decouple storage from compute, allowing for independent scaling of both and enabling features like instant branching and time travel.
Let’s see it in action. Imagine you have a Neon project with a database named my_db. You’ve got a Python application running on a serverless platform (like AWS Lambda or Vercel) that needs to interact with this database.
import os
import neon
# Fetch connection details from environment variables
NEON_HOST = os.environ.get("NEON_HOST")
NEON_PORT = os.environ.get("NEON_PORT", 5432)
NEON_USER = os.environ.get("NEON_USER")
NEON_PASSWORD = os.environ.get("NEON_PASSWORD")
NEON_DATABASE = os.environ.get("NEON_DATABASE")
# Construct the connection string
conn_string = f"postgresql://{NEON_USER}:{NEON_PASSWORD}@{NEON_HOST}:{NEON_PORT}/{NEON_DATABASE}?sslmode=require"
try:
# Establish the connection
conn = neon.connect(conn_string)
cursor = conn.cursor()
# Execute a query
cursor.execute("SELECT version();")
db_version = cursor.fetchone()
print(f"Connected to PostgreSQL version: {db_version[0]}")
# Example: Insert some data
cursor.execute("CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(100));")
cursor.execute("INSERT INTO users (name) VALUES (%s);", ("Alice",))
conn.commit()
print("Inserted user 'Alice'.")
# Example: Fetch data
cursor.execute("SELECT * FROM users;")
users = cursor.fetchall()
print("Current users:")
for user in users:
print(user)
cursor.close()
conn.close()
except Exception as e:
print(f"An error occurred: {e}")
When you run this code, your serverless function makes a request to Neon’s API to get a connection endpoint. This endpoint is a dynamically provisioned compute instance that interfaces with the shared storage layer. The actual data isn’t copied to your compute; instead, the compute instance reads directly from the distributed storage.
Neon separates the traditional monolithic PostgreSQL database into several key components:
- Compute Instances: These are stateless, ephemeral PostgreSQL instances. When your application connects, Neon provisions a compute instance on demand. This instance handles query execution, transaction processing, and session management. Because they are stateless, they can be spun up and down rapidly, which is crucial for serverless scalability.
- Shared Storage: This is the core innovation. Instead of each compute instance having its own data files, all compute instances in a Neon project share a common, distributed storage layer. This layer is typically built on object storage services (like AWS S3) and a log-structured merge-tree (LSM tree) implementation. It stores data in immutable "pages" and uses a WAL (Write-Ahead Log) to manage changes.
- Control Plane: This is the brain of Neon. It manages the lifecycle of compute instances, orchestrates storage operations, handles authentication, and provides the API for users to interact with their databases (e.g., creating databases, branching, managing settings).
- Branching: This is a direct consequence of the decoupled architecture. A "branch" in Neon is essentially a new compute instance that shares the same underlying storage as its parent branch. It only stores differences (new writes) in its own dedicated storage space. This allows for near-instantaneous creation of isolated development or testing environments without data duplication.
- Autoscaling: The control plane monitors the load on compute instances. When demand increases, it can automatically provision more compute instances. When demand drops, it scales down, ensuring you only pay for the compute you actively use.
The primary problem Neon solves is the rigidity and cost associated with traditional relational databases, especially in dynamic, serverless environments. It provides the familiar SQL interface and ACID guarantees of PostgreSQL but with the elasticity and cost-efficiency of modern cloud-native services. The shared storage model is what enables the "magic" of instant branching and efficient scaling. When a new branch is created, it doesn’t involve copying terabytes of data; it’s more like creating a new view or a copy-on-write snapshot of the storage, with new writes going to a separate, smaller delta.
The most surprising aspect of Neon’s architecture is how it achieves high performance despite the shared, distributed storage layer. By using a WAL-based approach and intelligent caching mechanisms within the compute instances, it minimizes the latency of reading from object storage. The compute instances effectively act as sophisticated caches and execution engines that pull data from the shared layer as needed, rather than being tightly coupled to specific disk locations.
The next step in understanding Neon is exploring its object-relational mapping (ORM) integration patterns.