-
Notifications
You must be signed in to change notification settings - Fork 389
Add sqlite_update_hook support #4234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
All contributors have signed the CLA ✍️ ✅ |
I have read the CLA Document and I hereby sign the CLA |
Hi @ruifigueira, thanks for contributing! To set expectations a bit, normally for a feature like this we would start with a design doc proposing an API and spend some time Your PR description shows an example, which is great, but doesn't directly describe the new API is. Would you mind writing that down to make this easier to review? Just write a reply on this thread containing roughly what reference documentation for this API might look like... |
@kentonv my expectations are very low, and I know this is a very basic approach to a very complex problem, but as I didn't see any reference in this repository to sqlite hooks, I just thought to share my exercise, which honestly was more for me to get confortable with Neverthelesss, here is my initial proposal, extending it to all sqlite hooks (for instance, sqlite session extension uses SQL Storage Events API ProposalThis API allows database operations monitoring and interception at various stages of execution. Types// integer if Number.MIN_SAFE_INTEGER <= rowId <= Number.MAX_SAFE_INTEGER,
// or bigint otherwise
type RowId = number | bigint;
// See https://sqlite.org/c3ref/preupdate_blobwrite.html
interface SqlStoragePreUpdateEvent extends Event {
action: 'insert' | 'update' | 'delete';
databaseName: string;
tableName: string;
rowId: RowId;
newRowId?:RowId;
// from sqlite3_preupdate_count
get columnsCount(): number;
// from sqlite3_preupdate_depth
get depth(): number;
// sqlite3_preupdate_old for each updated column (update or delete).
// Throws exception if called on an insert event
getOldValues<T extends Record<string, SqlStorageValue>>(): T;
// sqlite3_preupdate_new for each updated column (insert or update).
// Throws exception if called on an delete event
getNewValues<T extends Record<string, SqlStorageValue>>(): T;
}
// See https://sqlite.org/c3ref/preupdate_blobwrite.html
interface SqlStorageUpdateEvent extends Event {
action: 'insert' | 'update' | 'delete';
databaseName: string;
tableName: string;
rowId: RowId;
}
// See https://sqlite.org/c3ref/commit_hook.html
interface SqlStorageCommitEvent extends Event {
// forces a rollback by returning a non-zero value
// on sqlite3_commit_hook callback
rollback(): void;
}
type SqlStorageEventMap = {
// sqlite3_preupdate_hook
preupdate: SqlStoragePreUpdateEvent;
// sqlite3_update_hook
update: SqlStorageUpdateEvent;
// sqlite3_commit_hook
commit: SqlStorageCommitEvent;
// sqlite3_rollback_hook
rollback: Event;
};
interface SqlStorage extends EventTarget<SqlStorageEventMap> {
//...
} |
First of all, this started as an exercise, as I'm trying to learn a bit of cloudflare workers internals, so feel free to reject this PR.
Nevertheless, the use case I have in mind is to easily be able to react to SQLite changes. For instance, we can have a websocket sending every change that occurs in a table by just listening to
update
events that are triggered via sqlite_update_hook.Those events are synchronous, so with as little latency as sqlite / workerd allows.
Here's an example on how to use it:
I did some quick performance tests comparing my built
workerd
with and without this changes and, with no registered listener, performance is basically the same, and with listeners I only noticed a small increase, maybe 5%.It's not possible to run sql queries inside the listener yet (it crashes because
currentParseContext
prevents nested sql calls).