Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.2] - 2026-09-04

### Fixed

- Database integration tests now serialize direct extension provisioning with
`createIndex`, preventing concurrent `CREATE EXTENSION` calls from racing on
fresh CI databases.

## [0.1.1] - 2026-09-04

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "searchgres",
"version": "0.1.1",
"version": "0.1.2",
"description": "Postgres-native hybrid search: semantic + BM25 retrieval with composable hierarchy, metadata, temporal, and regex filtering.",
"license": "Apache-2.0",
"type": "module",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/create-index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type postgres from "postgres";
import { type IndexConfig, normalizeIndexConfig } from "./config.ts";
import { ensureExtension } from "./db/extensions.ts";
import { acquireAdvisoryLock, advisoryLockKey } from "./db/lock.ts";
import { acquireAdvisoryLock, CREATE_INDEX_LOCK_KEY } from "./db/lock.ts";
import { ensurePostgresVersion } from "./db/preflight.ts";
import { createBatchUpsertRoutine } from "./db/routines/batch-upsert.ts";
import { createRecordRoutines } from "./db/routines/records.ts";
Expand Down Expand Up @@ -43,7 +43,7 @@ export async function createIndex(
...DEFAULT_MIGRATION_TIMEOUTS,
lockTimeout: DEFAULT_MIGRATION_LOCK_TIMEOUT,
});
await acquireAdvisoryLock(tx, advisoryLockKey("searchgres:create-index"));
await acquireAdvisoryLock(tx, CREATE_INDEX_LOCK_KEY);
await setLockTimeout(tx, DEFAULT_MIGRATION_TIMEOUTS.lockTimeout);
await ensurePostgresVersion(tx);

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/db/lock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export function advisoryLockKey(name: string): AdvisoryLockKey {
return [digest.readInt32BE(0), digest.readInt32BE(4)];
}

/** Serializes index provisioning, including required extension installation. */
export const CREATE_INDEX_LOCK_KEY = advisoryLockKey("searchgres:create-index");

/** Block transaction-locally until an advisory lock is acquired or times out. */
export async function acquireAdvisoryLock(
sql: postgres.ISql,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
* This is package metadata only. Immutable index schemas use their own format
* marker and never derive compatibility from the library package version.
*/
export const LIBRARY_VERSION = "0.1.1";
export const LIBRARY_VERSION = "0.1.2";
11 changes: 10 additions & 1 deletion packages/core/test/db.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
type ExtensionRequirement,
ensureExtension,
} from "../src/db/extensions.ts";
import { acquireAdvisoryLock, advisoryLockKey } from "../src/db/lock.ts";
import {
acquireAdvisoryLock,
advisoryLockKey,
CREATE_INDEX_LOCK_KEY,
} from "../src/db/lock.ts";
import {
ensurePostgresVersion,
MINIMUM_POSTGRES_VERSION_NUM,
Expand Down Expand Up @@ -39,6 +43,11 @@ test("accepts PostgreSQL 18", async () => {

test("ensures supplied extensions with an empty search path", async () => {
const extensions = await sql.begin(async (tx) => {
// This test provisions extensions directly while other test files may call
// createIndex against the same fresh database. Honor ensureExtension's
// caller-owned locking contract so those CREATE EXTENSION statements
// cannot race.
await acquireAdvisoryLock(tx, CREATE_INDEX_LOCK_KEY);
await tx.unsafe("set local search_path to ''");
const results = [];
for (const requirement of requiredExtensions) {
Expand Down