@shivaedev/sql 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +4 -19
- package/dist/adapters/makePostgresClient.d.ts +1 -1
- package/dist/adapters/makePostgresClient.d.ts.map +1 -1
- package/dist/adapters/makePostgresClient.js +11 -9
- package/dist/adapters/makePostgresClient.js.map +1 -1
- package/dist/postgresSettings.d.ts +5 -0
- package/dist/postgresSettings.d.ts.map +1 -0
- package/dist/postgresSettings.js +72 -0
- package/dist/postgresSettings.js.map +1 -0
- package/package.json +7 -7
- package/src/adapters/makePostgresClient.ts +23 -21
- package/src/postgresSettings.ts +84 -0
- package/dist/applyLowMemoryPostgres.d.ts +0 -7
- package/dist/applyLowMemoryPostgres.d.ts.map +0 -1
- package/dist/applyLowMemoryPostgres.js +0 -24
- package/dist/applyLowMemoryPostgres.js.map +0 -1
- package/src/applyLowMemoryPostgres.ts +0 -28
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.45.0 - 2026-11-06
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- Drop calls and imports of the removed `applyLowMemoryPostgres`. Move desired server values into the host's required `postgresSettings` declaration, typed by `PostgresSettings` from `@shivaedev/sql/postgresSettings.ts`.
|
|
8
|
+
|
|
3
9
|
## 0.44.0 - 2026-11-06
|
|
4
10
|
|
|
5
11
|
### Breaking
|
package/README.md
CHANGED
|
@@ -29,28 +29,13 @@ Migrations are ordinary Effects loaded by native Migrator. `migratePostgres` add
|
|
|
29
29
|
|
|
30
30
|
The work has three parts: describe the model and provide its database once, choose repository operations per feature, and put each changed write inside the transaction that owns its commit.
|
|
31
31
|
|
|
32
|
-
###
|
|
32
|
+
### PostgreSQL server settings
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Applications declare server settings through `postgresSettings` on server's [`appDatabase`](https://github.com/ShivaeDev/platform/tree/main/packages/server#postgresql-server-settings) and database-backed hosts. SQL owns the `PostgresSettings` type and `applyPostgresSettings` in `@shivaedev/sql/postgresSettings.ts`; the host runs the applier during database startup, before migrations, outside their transaction. Settings describe the cluster's current configuration, rather than numbered migration history. The declaration maps PostgreSQL setting names to string values, including PostgreSQL units; it has no preset or implicit settings.
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
import { Config, Effect } from "effect";
|
|
38
|
-
import { applyLowMemoryPostgres } from "@shivaedev/sql/applyLowMemoryPostgres.ts";
|
|
39
|
-
import { postgresLayer } from "@shivaedev/sql/postgresLayer.ts";
|
|
40
|
-
|
|
41
|
-
const configure = Effect.gen(function* () {
|
|
42
|
-
const url = yield* Config.Redacted("DATABASE_URL");
|
|
43
|
-
yield* applyLowMemoryPostgres({ pointInTimeRecovery: false }).pipe(Effect.provide(postgresLayer(url)));
|
|
44
|
-
}).pipe(Effect.scoped);
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Restart PostgreSQL after this operation. `shared_buffers=32MB`, `max_connections=50` and `wal_buffers=4MB` take effect on restart. The reload settings are `jit=off`, `max_parallel_workers_per_gather=0`, `hash_mem_multiplier=1`, `autovacuum_max_workers=2`, `autovacuum_work_mem=32MB`, `idle_in_transaction_session_timeout=10min` and `wal_compression=zstd`. `idle_session_timeout=0` keeps idle LISTEN connections alive. Use PostgreSQL 18 with zstd support; older versions can require a restart for additional settings. Command-line `-c` settings override `ALTER SYSTEM`, so remove conflicting overrides. A failed operation can leave earlier settings written; correct the reported SQL error before restarting.
|
|
48
|
-
|
|
49
|
-
Point-in-time recovery is an explicit per-app memory and storage choice. With `pointInTimeRecovery: false`, the preset disables WAL archiving with `archive_mode=off`; it leaves `archive_timeout` alone and needs no backup bucket or pgBackRest configuration. Remove the host's archive and backup jobs and bucket configuration as well. This choice gives up recovery to a time between backups. With `true`, retain the host's working WAL archiving, backup bucket and restore procedure; the preset sets `archive_timeout=300` seconds. It does not provision backups or enable archiving. A timeout of 60 seconds can force a 16 MB WAL segment each minute even when the app is idle.
|
|
50
|
-
|
|
51
|
-
For PITR hosts, pgBackRest derives its process count from the CPU limit. Set a CPU limit of 2 or explicitly set `PGBACKREST_BACKUP_PROCESS_MAX=1` on the PostgreSQL service. Eight processes under a 32-vCPU limit exhausted a 477 MiB container in the reported Railway workload. Declare both CPU and memory limits in infrastructure as code: an omitted limit can be reset by the next plan. Keep enough headroom for backups, maintenance and the app's total connection budget across replicas and workers.
|
|
36
|
+
The applier reads `pg_settings`, compares live values with the declaration, writes only differences with `ALTER SYSTEM SET`, and requests `pg_reload_conf()`. Equivalent numeric, memory and time units compare equally. A restart-only change logs its setting name and wanted value as pending; the live value remains until PostgreSQL restarts. These settings affect every database in the cluster, so apps sharing a cluster must agree on them.
|
|
52
37
|
|
|
53
|
-
|
|
38
|
+
`ALTER SYSTEM` requires a superuser for these server settings. On a non-superuser deployment, or when PostgreSQL refuses a value, startup warns with the setting name, live value and wanted value, then continues starting the app. Manage those settings through the database operator when the app cannot change them.
|
|
54
39
|
|
|
55
40
|
### One PostgreSQL pool per application
|
|
56
41
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
|
-
import
|
|
2
|
+
import * as PgConnection from "@effect/sql-pg/PgConnection";
|
|
3
3
|
import * as PgPool from "@effect/sql-pg/PgPool";
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
import { SqlClient, Statement } from "effect/sql";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makePostgresClient.d.ts","sourceRoot":"","sources":["../../src/adapters/makePostgresClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"makePostgresClient.d.ts","sourceRoot":"","sources":["../../src/adapters/makePostgresClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,YAAY,MAAM,6BAA6B,CAAC;AAC5D,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAqB,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,SAAS,EAAsB,SAAS,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAgB,MAAM,qBAAqB,CAAC;AAI7D,eAAO,MAAM,kBAAkB;;;kBA0Bf,OAAO;sBACH,MAAM;sBASN,MAAM,WAAW,MAAM;;4EAIzC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
|
+
import * as PgConnection from "@effect/sql-pg/PgConnection";
|
|
2
3
|
import * as PgPool from "@effect/sql-pg/PgPool";
|
|
3
4
|
import { Effect, Predicate, Stream } from "effect";
|
|
4
5
|
import { SqlClient, Statement } from "effect/sql";
|
|
@@ -6,11 +7,11 @@ import { SqlError, UnknownError } from "effect/sql/SqlError";
|
|
|
6
7
|
const json = Statement.custom("PgJson");
|
|
7
8
|
export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function* (config) {
|
|
8
9
|
const pool = yield* PgPool.PgPool;
|
|
9
|
-
const acquirer = Effect.
|
|
10
|
+
const acquirer = Effect.flatMap(pool.get, (connection) => postgresConnection.pipe(Effect.provideService(PgConnection.PgConnection, connection)));
|
|
10
11
|
const client = yield* SqlClient.make({
|
|
11
12
|
acquirer,
|
|
12
|
-
borrower: (use) => pool.use((connection) =>
|
|
13
|
-
commit,
|
|
13
|
+
borrower: (use) => pool.use((connection) => Effect.flatMap(postgresConnection.pipe(Effect.provideService(PgConnection.PgConnection, connection)), use)),
|
|
14
|
+
commit: (connection) => Effect.flatMap(connection.executeRaw("COMMIT", []), commitResult),
|
|
14
15
|
compiler: PgClient.makeCompiler(config.transformQueryNames),
|
|
15
16
|
driver: "pg",
|
|
16
17
|
prepareTransactionControls: true,
|
|
@@ -21,7 +22,7 @@ export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function*
|
|
|
21
22
|
["server.address", config.host ?? "localhost"],
|
|
22
23
|
["server.port", config.port ?? 5432],
|
|
23
24
|
],
|
|
24
|
-
transactionAcquirer: Effect.
|
|
25
|
+
transactionAcquirer: Effect.flatMap(pool.reserve, (connection) => postgresConnection.pipe(Effect.provideService(PgConnection.PgConnection, connection))),
|
|
25
26
|
transformRows: config.transformResultNames === undefined ? undefined : Statement.defaultTransforms(config.transformResultNames).array,
|
|
26
27
|
});
|
|
27
28
|
return Object.assign(client, {
|
|
@@ -40,7 +41,8 @@ export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function*
|
|
|
40
41
|
poolStats: pool.stats,
|
|
41
42
|
});
|
|
42
43
|
});
|
|
43
|
-
|
|
44
|
+
const postgresConnection = Effect.gen(function* () {
|
|
45
|
+
const connection = yield* PgConnection.PgConnection;
|
|
44
46
|
function execute(sql, params, transformRows, prepare = true) {
|
|
45
47
|
const rows = Effect.map(connection.query(sql, params, prepare), (result) => result.rows);
|
|
46
48
|
return transformRows === undefined ? rows : Effect.map(rows, transformRows);
|
|
@@ -56,9 +58,9 @@ function postgresConnection(connection) {
|
|
|
56
58
|
executeValues: (sql, params) => connection.queryValues(sql, params),
|
|
57
59
|
executeValuesUnprepared: (sql, params) => connection.queryValues(sql, params, false),
|
|
58
60
|
};
|
|
59
|
-
}
|
|
60
|
-
function
|
|
61
|
-
return
|
|
61
|
+
});
|
|
62
|
+
function commitResult(result) {
|
|
63
|
+
return Predicate.hasProperty(result, "command") && result.command === "ROLLBACK"
|
|
62
64
|
? Effect.fail(new SqlError({
|
|
63
65
|
reason: new UnknownError({
|
|
64
66
|
cause: new Error("COMMIT rolled back an aborted transaction"),
|
|
@@ -66,6 +68,6 @@ function commit(connection) {
|
|
|
66
68
|
operation: "commit",
|
|
67
69
|
}),
|
|
68
70
|
}))
|
|
69
|
-
: Effect.void
|
|
71
|
+
: Effect.void;
|
|
70
72
|
}
|
|
71
73
|
//# sourceMappingURL=makePostgresClient.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"makePostgresClient.js","sourceRoot":"","sources":["../../src/adapters/makePostgresClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"makePostgresClient.js","sourceRoot":"","sources":["../../src/adapters/makePostgresClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
export type PostgresSettings = Readonly<Record<string, string>>;
|
|
4
|
+
export declare const applyPostgresSettings: (settings: Readonly<Record<string, string>>) => Effect.Effect<void, never, SqlClient.SqlClient>;
|
|
5
|
+
//# sourceMappingURL=postgresSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresSettings.d.ts","sourceRoot":"","sources":["../src/postgresSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAElD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAUhE,eAAO,MAAM,qBAAqB,iGA4ChC,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { SqlClient, SqlSchema } from "effect/sql";
|
|
3
|
+
const Setting = Schema.Struct({
|
|
4
|
+
context: Schema.String,
|
|
5
|
+
pendingRestart: Schema.Boolean,
|
|
6
|
+
setting: Schema.String,
|
|
7
|
+
unit: Schema.NullOr(Schema.String),
|
|
8
|
+
vartype: Schema.String,
|
|
9
|
+
}).pipe(Schema.encodeKeys({ pendingRestart: "pending_restart" }));
|
|
10
|
+
export const applyPostgresSettings = Effect.fn("Sql.applyPostgresSettings")(function* (settings) {
|
|
11
|
+
const sql = (yield* SqlClient.SqlClient).withoutTransforms();
|
|
12
|
+
const read = SqlSchema.findAll({
|
|
13
|
+
execute: (name) => sql `select setting, unit, vartype, context, pending_restart from pg_settings where name = ${name}`,
|
|
14
|
+
Request: Schema.String,
|
|
15
|
+
Result: Setting,
|
|
16
|
+
});
|
|
17
|
+
const changed = [];
|
|
18
|
+
for (const [name, wanted] of Object.entries(settings)) {
|
|
19
|
+
let live = "unavailable";
|
|
20
|
+
yield* Effect.gen(function* () {
|
|
21
|
+
const [current] = yield* read(name);
|
|
22
|
+
if (current === undefined) {
|
|
23
|
+
return yield* Effect.logWarning("PostgreSQL setting is unknown", { live, setting: name, wanted });
|
|
24
|
+
}
|
|
25
|
+
live = `${current.setting}${current.unit === null ? "" : ` ${current.unit}`}`;
|
|
26
|
+
if (yield* settingMatches(current, wanted)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
yield* sql.unsafe(`ALTER SYSTEM SET "${name.replaceAll('"', '""')}" = '${wanted.replaceAll("'", "''")}'`).unprepared;
|
|
30
|
+
changed.push({ live, name, restart: current.context === "postmaster", wanted });
|
|
31
|
+
}).pipe(Effect.catch((error) => Effect.logWarning("Could not apply PostgreSQL setting", { error, live, setting: name, wanted })));
|
|
32
|
+
}
|
|
33
|
+
if (changed.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
yield* sql `select pg_reload_conf()`.pipe(Effect.andThen(Effect.forEach(changed, (change) => Effect.gen(function* () {
|
|
37
|
+
const [current] = yield* read(change.name);
|
|
38
|
+
// Reload signals are asynchronous; postmaster settings written above already require a restart.
|
|
39
|
+
if (current?.pendingRestart || change.restart) {
|
|
40
|
+
yield* Effect.logWarning("PostgreSQL setting pending restart", { setting: change.name, wanted: change.wanted });
|
|
41
|
+
}
|
|
42
|
+
}))), Effect.catch((error) => Effect.forEach(changed, ({ name, live, wanted }) => Effect.logWarning("Could not reload PostgreSQL setting", { error, live, setting: name, wanted }))));
|
|
43
|
+
});
|
|
44
|
+
const settingMatches = Effect.fnUntraced(function* (current, wanted) {
|
|
45
|
+
if (current.setting === wanted) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const sql = yield* SqlClient.SqlClient;
|
|
49
|
+
let comparison = sql `${current.setting} = ${wanted}`;
|
|
50
|
+
if (current.vartype === "bool") {
|
|
51
|
+
comparison = sql `${current.setting}::boolean = ${wanted}::boolean`;
|
|
52
|
+
}
|
|
53
|
+
else if (["integer", "real"].includes(current.vartype)) {
|
|
54
|
+
if (/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/iu.test(wanted.trim())) {
|
|
55
|
+
comparison = sql `${current.setting}::numeric = ${wanted}::numeric`;
|
|
56
|
+
}
|
|
57
|
+
else if (["B", "kB", "8kB", "MB"].includes(current.unit ?? "")) {
|
|
58
|
+
const unit = current.unit === "8kB" ? "8kB" : `1${current.unit}`;
|
|
59
|
+
comparison = sql `${current.setting}::numeric * pg_size_bytes(${unit}) = pg_size_bytes(${wanted})`;
|
|
60
|
+
}
|
|
61
|
+
else if (["us", "ms", "s", "min", "h", "d"].includes(current.unit ?? "")) {
|
|
62
|
+
comparison = sql `${current.setting}::numeric * extract(epoch from ${`1 ${current.unit}`}::interval) = extract(epoch from ${wanted}::interval)`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const [row] = yield* SqlSchema.findAll({
|
|
66
|
+
execute: () => sql `select ${comparison} as matches`,
|
|
67
|
+
Request: Schema.Void,
|
|
68
|
+
Result: Schema.Struct({ matches: Schema.Boolean }),
|
|
69
|
+
})();
|
|
70
|
+
return row?.matches === true;
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=postgresSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresSettings.js","sourceRoot":"","sources":["../src/postgresSettings.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "Schema-derived repositories over native Effect SQL",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect"
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"@effect/platform-node": "4.1.0",
|
|
51
51
|
"@effect/sql-pg": "4.1.0",
|
|
52
52
|
"@effect/sql-sqlite-node": "4.1.0",
|
|
53
|
-
"@shivaedev/contract": "0.
|
|
54
|
-
"@shivaedev/jobs": "0.
|
|
55
|
-
"@shivaedev/quality": "0.
|
|
56
|
-
"@shivaedev/test-kits": "0.9.
|
|
57
|
-
"@shivaedev/test-sql": "0.24.
|
|
58
|
-
"@shivaedev/test-story": "0.
|
|
53
|
+
"@shivaedev/contract": "0.42.0",
|
|
54
|
+
"@shivaedev/jobs": "0.41.0",
|
|
55
|
+
"@shivaedev/quality": "0.69.1",
|
|
56
|
+
"@shivaedev/test-kits": "0.9.3",
|
|
57
|
+
"@shivaedev/test-sql": "0.24.3",
|
|
58
|
+
"@shivaedev/test-story": "0.44.2",
|
|
59
59
|
"@types/node": "24.19.2",
|
|
60
60
|
"effect": "4.1.0",
|
|
61
61
|
"typescript": "7.0.2",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as PgClient from "@effect/sql-pg/PgClient";
|
|
2
|
-
import
|
|
2
|
+
import * as PgConnection from "@effect/sql-pg/PgConnection";
|
|
3
3
|
import * as PgPool from "@effect/sql-pg/PgPool";
|
|
4
4
|
import { Effect, Predicate, Stream } from "effect";
|
|
5
5
|
import { SqlClient, type SqlConnection, Statement } from "effect/sql";
|
|
@@ -9,11 +9,12 @@ const json = Statement.custom<PgClient.PgCustom>("PgJson");
|
|
|
9
9
|
|
|
10
10
|
export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function* (config: PgClient.PgClientConfig) {
|
|
11
11
|
const pool = yield* PgPool.PgPool;
|
|
12
|
-
const acquirer = Effect.
|
|
12
|
+
const acquirer = Effect.flatMap(pool.get, (connection) => postgresConnection.pipe(Effect.provideService(PgConnection.PgConnection, connection)));
|
|
13
13
|
const client = yield* SqlClient.make({
|
|
14
14
|
acquirer,
|
|
15
|
-
borrower: (use) =>
|
|
16
|
-
|
|
15
|
+
borrower: (use) =>
|
|
16
|
+
pool.use((connection) => Effect.flatMap(postgresConnection.pipe(Effect.provideService(PgConnection.PgConnection, connection)), use)),
|
|
17
|
+
commit: (connection) => Effect.flatMap(connection.executeRaw("COMMIT", []), commitResult),
|
|
17
18
|
compiler: PgClient.makeCompiler(config.transformQueryNames),
|
|
18
19
|
driver: "pg",
|
|
19
20
|
prepareTransactionControls: true,
|
|
@@ -24,7 +25,9 @@ export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function*
|
|
|
24
25
|
["server.address", config.host ?? "localhost"],
|
|
25
26
|
["server.port", config.port ?? 5432],
|
|
26
27
|
],
|
|
27
|
-
transactionAcquirer: Effect.
|
|
28
|
+
transactionAcquirer: Effect.flatMap(pool.reserve, (connection) =>
|
|
29
|
+
postgresConnection.pipe(Effect.provideService(PgConnection.PgConnection, connection)),
|
|
30
|
+
),
|
|
28
31
|
transformRows: config.transformResultNames === undefined ? undefined : Statement.defaultTransforms(config.transformResultNames).array,
|
|
29
32
|
});
|
|
30
33
|
return Object.assign(client, {
|
|
@@ -46,7 +49,8 @@ export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function*
|
|
|
46
49
|
}) satisfies PgClient.PgClient;
|
|
47
50
|
});
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
const postgresConnection = Effect.gen(function* () {
|
|
53
|
+
const connection = yield* PgConnection.PgConnection;
|
|
50
54
|
function execute(sql: string, params: readonly unknown[], transformRows: Parameters<SqlConnection.Connection["execute"]>[2], prepare = true) {
|
|
51
55
|
const rows = Effect.map(connection.query(sql, params, prepare), (result) => result.rows);
|
|
52
56
|
return transformRows === undefined ? rows : Effect.map(rows, transformRows);
|
|
@@ -61,21 +65,19 @@ function postgresConnection(connection: PgConnection.PgConnection): SqlConnectio
|
|
|
61
65
|
executeUnprepared: (sql, params, transformRows) => execute(sql, params, transformRows, false),
|
|
62
66
|
executeValues: (sql, params) => connection.queryValues(sql, params),
|
|
63
67
|
executeValuesUnprepared: (sql, params) => connection.queryValues(sql, params, false),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
68
|
+
} satisfies SqlConnection.Connection;
|
|
69
|
+
});
|
|
66
70
|
|
|
67
|
-
function
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
new
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
operation: "commit",
|
|
76
|
-
}),
|
|
71
|
+
function commitResult(result: unknown) {
|
|
72
|
+
return Predicate.hasProperty(result, "command") && result.command === "ROLLBACK"
|
|
73
|
+
? Effect.fail(
|
|
74
|
+
new SqlError({
|
|
75
|
+
reason: new UnknownError({
|
|
76
|
+
cause: new Error("COMMIT rolled back an aborted transaction"),
|
|
77
|
+
message: "PgClient: COMMIT rolled back an aborted transaction",
|
|
78
|
+
operation: "commit",
|
|
77
79
|
}),
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
}),
|
|
81
|
+
)
|
|
82
|
+
: Effect.void;
|
|
81
83
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { SqlClient, SqlSchema } from "effect/sql";
|
|
3
|
+
|
|
4
|
+
export type PostgresSettings = Readonly<Record<string, string>>;
|
|
5
|
+
|
|
6
|
+
const Setting = Schema.Struct({
|
|
7
|
+
context: Schema.String,
|
|
8
|
+
pendingRestart: Schema.Boolean,
|
|
9
|
+
setting: Schema.String,
|
|
10
|
+
unit: Schema.NullOr(Schema.String),
|
|
11
|
+
vartype: Schema.String,
|
|
12
|
+
}).pipe(Schema.encodeKeys({ pendingRestart: "pending_restart" }));
|
|
13
|
+
|
|
14
|
+
export const applyPostgresSettings = Effect.fn("Sql.applyPostgresSettings")(function* (settings: PostgresSettings) {
|
|
15
|
+
const sql = (yield* SqlClient.SqlClient).withoutTransforms();
|
|
16
|
+
const read = SqlSchema.findAll({
|
|
17
|
+
execute: (name) => sql`select setting, unit, vartype, context, pending_restart from pg_settings where name = ${name}`,
|
|
18
|
+
Request: Schema.String,
|
|
19
|
+
Result: Setting,
|
|
20
|
+
});
|
|
21
|
+
const changed: Array<{ name: string; live: string; wanted: string; restart: boolean }> = [];
|
|
22
|
+
for (const [name, wanted] of Object.entries(settings)) {
|
|
23
|
+
let live = "unavailable";
|
|
24
|
+
yield* Effect.gen(function* () {
|
|
25
|
+
const [current] = yield* read(name);
|
|
26
|
+
if (current === undefined) {
|
|
27
|
+
return yield* Effect.logWarning("PostgreSQL setting is unknown", { live, setting: name, wanted });
|
|
28
|
+
}
|
|
29
|
+
live = `${current.setting}${current.unit === null ? "" : ` ${current.unit}`}`;
|
|
30
|
+
if (yield* settingMatches(current, wanted)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
yield* sql.unsafe(`ALTER SYSTEM SET "${name.replaceAll('"', '""')}" = '${wanted.replaceAll("'", "''")}'`).unprepared;
|
|
34
|
+
changed.push({ live, name, restart: current.context === "postmaster", wanted });
|
|
35
|
+
}).pipe(Effect.catch((error) => Effect.logWarning("Could not apply PostgreSQL setting", { error, live, setting: name, wanted })));
|
|
36
|
+
}
|
|
37
|
+
if (changed.length === 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
yield* sql`select pg_reload_conf()`.pipe(
|
|
41
|
+
Effect.andThen(
|
|
42
|
+
Effect.forEach(changed, (change) =>
|
|
43
|
+
Effect.gen(function* () {
|
|
44
|
+
const [current] = yield* read(change.name);
|
|
45
|
+
// Reload signals are asynchronous; postmaster settings written above already require a restart.
|
|
46
|
+
if (current?.pendingRestart || change.restart) {
|
|
47
|
+
yield* Effect.logWarning("PostgreSQL setting pending restart", { setting: change.name, wanted: change.wanted });
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
Effect.catch((error) =>
|
|
53
|
+
Effect.forEach(changed, ({ name, live, wanted }) =>
|
|
54
|
+
Effect.logWarning("Could not reload PostgreSQL setting", { error, live, setting: name, wanted }),
|
|
55
|
+
),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const settingMatches = Effect.fnUntraced(function* (current: typeof Setting.Type, wanted: string) {
|
|
61
|
+
if (current.setting === wanted) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
const sql = yield* SqlClient.SqlClient;
|
|
65
|
+
let comparison = sql`${current.setting} = ${wanted}`;
|
|
66
|
+
if (current.vartype === "bool") {
|
|
67
|
+
comparison = sql`${current.setting}::boolean = ${wanted}::boolean`;
|
|
68
|
+
} else if (["integer", "real"].includes(current.vartype)) {
|
|
69
|
+
if (/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/iu.test(wanted.trim())) {
|
|
70
|
+
comparison = sql`${current.setting}::numeric = ${wanted}::numeric`;
|
|
71
|
+
} else if (["B", "kB", "8kB", "MB"].includes(current.unit ?? "")) {
|
|
72
|
+
const unit = current.unit === "8kB" ? "8kB" : `1${current.unit}`;
|
|
73
|
+
comparison = sql`${current.setting}::numeric * pg_size_bytes(${unit}) = pg_size_bytes(${wanted})`;
|
|
74
|
+
} else if (["us", "ms", "s", "min", "h", "d"].includes(current.unit ?? "")) {
|
|
75
|
+
comparison = sql`${current.setting}::numeric * extract(epoch from ${`1 ${current.unit}`}::interval) = extract(epoch from ${wanted}::interval)`;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const [row] = yield* SqlSchema.findAll({
|
|
79
|
+
execute: () => sql`select ${comparison} as matches`,
|
|
80
|
+
Request: Schema.Void,
|
|
81
|
+
Result: Schema.Struct({ matches: Schema.Boolean }),
|
|
82
|
+
})();
|
|
83
|
+
return row?.matches === true;
|
|
84
|
+
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
export interface LowMemoryPostgresOptions {
|
|
4
|
-
readonly pointInTimeRecovery: boolean;
|
|
5
|
-
}
|
|
6
|
-
export declare const applyLowMemoryPostgres: (args_0: LowMemoryPostgresOptions) => Effect.Effect<void, import("effect/sql/SqlError").SqlError, SqlClient.SqlClient>;
|
|
7
|
-
//# sourceMappingURL=applyLowMemoryPostgres.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"applyLowMemoryPostgres.d.ts","sourceRoot":"","sources":["../src/applyLowMemoryPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACtC;AAED,eAAO,MAAM,sBAAsB,wHAoBjC,CAAC"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
export const applyLowMemoryPostgres = Effect.fn("Sql.applyLowMemoryPostgres")(function* ({ pointInTimeRecovery }) {
|
|
4
|
-
const sql = yield* SqlClient.SqlClient;
|
|
5
|
-
const settings = {
|
|
6
|
-
"autovacuum_max_workers": "2",
|
|
7
|
-
"autovacuum_work_mem": "32MB",
|
|
8
|
-
"hash_mem_multiplier": "1",
|
|
9
|
-
"idle_in_transaction_session_timeout": "10min",
|
|
10
|
-
"idle_session_timeout": "0",
|
|
11
|
-
jit: "off",
|
|
12
|
-
"max_connections": "50",
|
|
13
|
-
"max_parallel_workers_per_gather": "0",
|
|
14
|
-
"shared_buffers": "32MB",
|
|
15
|
-
"wal_buffers": "4MB",
|
|
16
|
-
"wal_compression": "zstd",
|
|
17
|
-
...(pointInTimeRecovery ? { "archive_timeout": "300" } : { "archive_mode": "off" }),
|
|
18
|
-
};
|
|
19
|
-
for (const [name, value] of Object.entries(settings)) {
|
|
20
|
-
yield* sql.unsafe(`ALTER SYSTEM SET ${name} = '${value}'`);
|
|
21
|
-
}
|
|
22
|
-
yield* sql `select pg_reload_conf()`;
|
|
23
|
-
});
|
|
24
|
-
//# sourceMappingURL=applyLowMemoryPostgres.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"applyLowMemoryPostgres.js","sourceRoot":"","sources":["../src/applyLowMemoryPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,EAAE,CAAC,4BAA4B,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,mBAAmB,EAA4B;IACzI,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,QAAQ,GAAG;QAChB,wBAAwB,EAAE,GAAG;QAC7B,qBAAqB,EAAE,MAAM;QAC7B,qBAAqB,EAAE,GAAG;QAC1B,qCAAqC,EAAE,OAAO;QAC9C,sBAAsB,EAAE,GAAG;QAC3B,GAAG,EAAE,KAAK;QACV,iBAAiB,EAAE,IAAI;QACvB,iCAAiC,EAAE,GAAG;QACtC,gBAAgB,EAAE,MAAM;QACxB,aAAa,EAAE,KAAK;QACpB,iBAAiB,EAAE,MAAM;QACzB,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;KACnF,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtD,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,oBAAoB,IAAI,OAAO,KAAK,GAAG,CAAC,CAAC;IAC5D,CAAC;IACD,KAAK,CAAC,CAAC,GAAG,CAAA,yBAAyB,CAAC;AACrC,CAAC,CAAC,CAAC"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/sql";
|
|
3
|
-
|
|
4
|
-
export interface LowMemoryPostgresOptions {
|
|
5
|
-
readonly pointInTimeRecovery: boolean;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const applyLowMemoryPostgres = Effect.fn("Sql.applyLowMemoryPostgres")(function* ({ pointInTimeRecovery }: LowMemoryPostgresOptions) {
|
|
9
|
-
const sql = yield* SqlClient.SqlClient;
|
|
10
|
-
const settings = {
|
|
11
|
-
"autovacuum_max_workers": "2",
|
|
12
|
-
"autovacuum_work_mem": "32MB",
|
|
13
|
-
"hash_mem_multiplier": "1",
|
|
14
|
-
"idle_in_transaction_session_timeout": "10min",
|
|
15
|
-
"idle_session_timeout": "0",
|
|
16
|
-
jit: "off",
|
|
17
|
-
"max_connections": "50",
|
|
18
|
-
"max_parallel_workers_per_gather": "0",
|
|
19
|
-
"shared_buffers": "32MB",
|
|
20
|
-
"wal_buffers": "4MB",
|
|
21
|
-
"wal_compression": "zstd",
|
|
22
|
-
...(pointInTimeRecovery ? { "archive_timeout": "300" } : { "archive_mode": "off" }),
|
|
23
|
-
};
|
|
24
|
-
for (const [name, value] of Object.entries(settings)) {
|
|
25
|
-
yield* sql.unsafe(`ALTER SYSTEM SET ${name} = '${value}'`);
|
|
26
|
-
}
|
|
27
|
-
yield* sql`select pg_reload_conf()`;
|
|
28
|
-
});
|