Connect to a Database
To start simple, create a PostgreSQL database and several tables there. You can also use you existing database:
$ psql postgresql://postgres:[email protected]/postgres -f ents/cluster.sql
CREATE TABLE users(
id bigserial PRIMARY KEY,
email varchar(256) NOT NULL UNIQUE,
is_admin boolean NOT NULL DEFAULT FALSE
);
CREATE TABLE topics(
id bigserial PRIMARY KEY,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
slug varchar(64) NOT NULL UNIQUE,
creator_id bigint NOT NULL,
subject text DEFAULT NULL
);
CREATE TABLE comments(
id bigserial PRIMARY KEY,
created_at timestamptz NOT NULL,
topic_id bigint REFERENCES topics,
creator_id bigint NOT NULL,
message text NOT NULL
);
CREATE TABLE organizations(
id bigserial PRIMARY KEY,
name text NOT NULL UNIQUE
);
CREATE TABLE organization_users(
id bigserial PRIMARY KEY,
organization_id bigint REFERENCES organizations,
user_id bigint REFERENCES users,
UNIQUE (organization_id, user_id)
);
To access that database, create an instance of Cluster:
import { Cluster } from "ent-framework";
import type { PgClientOptions } from "ent-framework/pg";
import { PgClient } from "ent-framework/pg";
import type { PoolConfig } from "pg";
export const cluster = new Cluster<PgClient, PgClientOptions>({
islands: async () => [ // sync or async
{
no: 0,
nodes: [
{
name: "island0-master",
config: {
connectionString: process.env.DATABASE_URL, // e.g. from .env
// This object is of the standard node-postgres type PoolConfig.
// Thus, you can use host, port, user, password, database and other
// properties instead of connectionString if you want.
min: 5,
max: 20,
} satisfies PoolConfig,
},
],
},
],
createClient: (node) => new PgClient(node),
loggers: {
clientQueryLogger: (props) => console.debug(props.msg),
swallowedErrorLogger: (props) => console.log(props),
},
});
// Pre-open min number of DB connections.
cluster.prewarm();
Terminology:
Cluster consists of Islands. Each Island is identified by an integer number (there can be many islands for horizontal scaling of the cluster).
Island consists of master + replica nodes (in the above example, we only define one master node and no replicas).
Island also hosts Microshards (in the example above, we will have no microshards, aka just one global shard). Microshards may travel from island to island during shards rebalancing process; the engine tracks this automatically ("shards discovery").
Notice that we define the layout of the cluster using a callback. Ent Framework will call it from time to time to refresh the view of the cluster, so in this callback, you can read the data from some centralized configuration database (new nodes may be added, or empty nodes may be removed with no downtime). This is called "dynamic real-time reconfiguration".
PgClient class accepts several options, one of them is the standard node-postgres PoolConfig interface. For simplicity, when we define a cluster shape in islands
, we just return a list of such configs, to be passed into createClient()
lambda.
As of prewarm()
call, it's explained in Advanced section.
Last updated
Was this helpful?