Setup simple migration system (#7)

* Setup migrations, misc tidy/fixes

* Simplify migration provider
This commit is contained in:
devin ivy 2023-05-11 00:00:44 -04:00 committed by GitHub
parent 50f764ba86
commit 71c2ee061e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 35 deletions

View File

@ -1,5 +1,7 @@
import { Kysely, SqliteDialect } from 'kysely'
import SqliteDb from 'better-sqlite3'
import { Kysely, Migrator, SqliteDialect } from 'kysely'
import { DatabaseSchema } from './schema'
import { migrationProvider } from './migrations'
export const createDb = (location: string): Database => {
return new Kysely<DatabaseSchema>({
@ -9,22 +11,10 @@ export const createDb = (location: string): Database => {
})
}
export const migrateToLatest = async (db: Database) => {
const migrator = new Migrator({ db, provider: migrationProvider })
const { error } = await migrator.migrateToLatest()
if (error) throw error
}
export type Database = Kysely<DatabaseSchema>
export type PostTable = {
uri: string
cid: string
replyParent: string | null
replyRoot: string | null
indexedAt: string
}
export type SubStateTable = {
service: string
cursor: number
}
export type DatabaseSchema = {
posts: PostTable
sub_state: SubStateTable
}

31
src/db/migrations.ts Normal file
View File

@ -0,0 +1,31 @@
import { Kysely, Migration, MigrationProvider } from 'kysely'
const migrations: Record<string, Migration> = {}
export const migrationProvider: MigrationProvider = {
async getMigrations() {
return migrations
},
}
migrations['001'] = {
async up(db: Kysely<unknown>) {
await db.schema
.createTable('post')
.addColumn('uri', 'varchar', (col) => col.primaryKey())
.addColumn('cid', 'varchar', (col) => col.notNull())
.addColumn('replyParent', 'varchar')
.addColumn('replyRoot', 'varchar')
.addColumn('indexedAt', 'varchar', (col) => col.notNull())
.execute()
await db.schema
.createTable('sub_state')
.addColumn('service', 'varchar', (col) => col.primaryKey())
.addColumn('cursor', 'integer', (col) => col.notNull())
.execute()
},
async down(db: Kysely<unknown>) {
await db.schema.dropTable('post').execute()
await db.schema.dropTable('sub_state').execute()
},
}

17
src/db/schema.ts Normal file
View File

@ -0,0 +1,17 @@
export type DatabaseSchema = {
post: Post
sub_state: SubState
}
export type Post = {
uri: string
cid: string
replyParent: string | null
replyRoot: string | null
indexedAt: string
}
export type SubState = {
service: string
cursor: number
}

View File

@ -8,7 +8,7 @@ export default function (server: Server, db: Database) {
throw new InvalidRequestError('algorithm unsupported')
}
let builder = db
.selectFrom('posts')
.selectFrom('post')
.selectAll()
.orderBy('indexedAt', 'desc')
.orderBy('cid', 'desc')
@ -20,9 +20,9 @@ export default function (server: Server, db: Database) {
}
const timeStr = new Date(parseInt(indexedAt, 10)).toISOString()
builder = builder
.where('posts.indexedAt', '<', timeStr)
.orWhere((qb) => qb.where('posts.indexedAt', '=', timeStr))
.where('posts.cid', '<', cid)
.where('post.indexedAt', '<', timeStr)
.orWhere((qb) => qb.where('post.indexedAt', '=', timeStr))
.where('post.cid', '<', cid)
}
const res = await builder.execute()

View File

@ -9,7 +9,7 @@ const run = async () => {
subscriptionEndpoint: maybeStr(process.env.FEEDGEN_SUBSCRIPTION_ENDPOINT),
})
await server.start()
console.log(`🤖 running feed generator at localhost${server.cfg.port}`)
console.log(`🤖 running feed generator at localhost:${server.cfg.port}`)
}
const maybeStr = (val?: string) => {

View File

@ -3,7 +3,7 @@ import events from 'events'
import express from 'express'
import { createServer } from './lexicon'
import feedGeneration from './feed-generation'
import { createDb, Database } from './db'
import { createDb, Database, migrateToLatest } from './db'
import { FirehoseSubscription } from './subscription'
export type Config = {
@ -32,7 +32,7 @@ export class FeedGenerator {
}
static create(config?: Partial<Config>) {
const cfg = {
const cfg: Config = {
port: config?.port ?? 3000,
sqliteLocation: config?.sqliteLocation ?? 'test.sqlite',
subscriptionEndpoint: config?.subscriptionEndpoint ?? 'wss://bsky.social',
@ -56,12 +56,11 @@ export class FeedGenerator {
}
async start(): Promise<http.Server> {
await this.firehose.run()
const server = this.app.listen(this.cfg.port)
server.keepAliveTimeout = 90000
this.server = server
await events.once(server, 'listening')
return server
await migrateToLatest(this.db)
this.firehose.run()
this.server = this.app.listen(this.cfg.port)
await events.once(this.server, 'listening')
return this.server
}
}

View File

@ -36,13 +36,13 @@ export class FirehoseSubscription extends FirehoseSubscriptionBase {
if (postsToDelete.length > 0) {
await this.db
.deleteFrom('posts')
.deleteFrom('post')
.where('uri', 'in', postsToDelete)
.execute()
}
if (postsToCreate.length > 0) {
await this.db
.insertInto('posts')
.insertInto('post')
.values(postsToCreate)
.onConflict((oc) => oc.doNothing())
.execute()

View File

@ -78,7 +78,7 @@ export abstract class FirehoseSubscriptionBase {
export const getPostOperations = async (evt: Commit): Promise<Operations> => {
const ops: Operations = { creates: [], deletes: [] }
const postOps = evt.ops.filter(
(op) => op.path.split('/')[1] === ids.AppBskyFeedPost,
(op) => op.path.split('/')[0] === ids.AppBskyFeedPost,
)
if (postOps.length < 1) return ops