This removes the need for constant DB HEAD requests to check if a database exists or not. Instead, it tries to make the request, and if it fails it will check if the reason for failure is the database not existing. If it doesn't exist it runs through the same old flow to confirm that it definitely doesn't exist, and if it doesn't then it will create it.

This commit is contained in:
mike12345567 2024-01-11 16:44:15 +00:00
parent 1f958fcf53
commit 8483a53178
4 changed files with 113 additions and 792 deletions

View File

@ -19,6 +19,8 @@ import { WriteStream, ReadStream } from "fs"
import { newid } from "../../docIds/newid"
import { DDInstrumentedDatabase } from "../instrumentation"
const DATABASE_NOT_FOUND = "Database does not exist."
function buildNano(couchInfo: { url: string; cookie: string }) {
return Nano({
url: couchInfo.url,
@ -31,6 +33,8 @@ function buildNano(couchInfo: { url: string; cookie: string }) {
})
}
type DBCall = () => Promise<any>
export function DatabaseWithConnection(
dbName: string,
connection: string,
@ -78,7 +82,11 @@ export class DatabaseImpl implements Database {
return this.instanceNano || DatabaseImpl.nano
}
async checkSetup() {
private getDb() {
return this.nano().db.use(this.name)
}
private async checkAndCreateDb() {
let shouldCreate = !this.pouchOpts?.skip_setup
// check exists in a lightweight fashion
let exists = await this.exists()
@ -95,14 +103,22 @@ export class DatabaseImpl implements Database {
}
}
}
return this.nano().db.use(this.name)
return this.getDb()
}
private async updateOutput(fnc: any) {
// this function fetches the DB and handles if DB creation is needed
private async performCall(
call: (db: Nano.DocumentScope<any>) => Promise<DBCall> | DBCall
): Promise<any> {
const db = this.getDb()
const fnc = await call(db)
try {
return await fnc()
} catch (err: any) {
if (err.statusCode) {
if (err.statusCode === 404 && err.reason === DATABASE_NOT_FOUND) {
await this.checkAndCreateDb()
return await this.performCall(call)
} else if (err.statusCode) {
err.status = err.statusCode
}
throw err
@ -110,11 +126,12 @@ export class DatabaseImpl implements Database {
}
async get<T extends Document>(id?: string): Promise<T> {
const db = await this.checkSetup()
return this.performCall(db => {
if (!id) {
throw new Error("Unable to get doc without a valid _id.")
}
return this.updateOutput(() => db.get(id))
return () => db.get(id)
})
}
async getMultiple<T extends Document>(
@ -147,7 +164,7 @@ export class DatabaseImpl implements Database {
}
async remove(idOrDoc: string | Document, rev?: string) {
const db = await this.checkSetup()
return this.performCall(db => {
let _id: string
let _rev: string
@ -162,7 +179,8 @@ export class DatabaseImpl implements Database {
if (!_id || !_rev) {
throw new Error("Unable to remove doc without a valid _id and _rev.")
}
return this.updateOutput(() => db.destroy(_id, _rev))
return () => db.destroy(_id, _rev)
})
}
async post(document: AnyDocument, opts?: DatabasePutOpts) {
@ -176,7 +194,7 @@ export class DatabaseImpl implements Database {
if (!document._id) {
throw new Error("Cannot store document without _id field.")
}
const db = await this.checkSetup()
return this.performCall(async db => {
if (!document.createdAt) {
document.createdAt = new Date().toISOString()
}
@ -193,28 +211,32 @@ export class DatabaseImpl implements Database {
}
}
}
return this.updateOutput(() => db.insert(document))
return () => db.insert(document)
})
}
async bulkDocs(documents: AnyDocument[]) {
const db = await this.checkSetup()
return this.updateOutput(() => db.bulk({ docs: documents }))
return this.performCall(db => {
return () => db.bulk({ docs: documents })
})
}
async allDocs<T extends Document>(
params: DatabaseQueryOpts
): Promise<AllDocsResponse<T>> {
const db = await this.checkSetup()
return this.updateOutput(() => db.list(params))
return this.performCall(db => {
return () => db.list(params)
})
}
async query<T extends Document>(
viewName: string,
params: DatabaseQueryOpts
): Promise<AllDocsResponse<T>> {
const db = await this.checkSetup()
return this.performCall(db => {
const [database, view] = viewName.split("/")
return this.updateOutput(() => db.view(database, view, params))
return () => db.view(database, view, params)
})
}
async destroy() {
@ -231,8 +253,9 @@ export class DatabaseImpl implements Database {
}
async compact() {
const db = await this.checkSetup()
return this.updateOutput(() => db.compact())
return this.performCall(db => {
return () => db.compact()
})
}
// All below functions are in-frequently called, just utilise PouchDB

View File

@ -31,13 +31,6 @@ export class DDInstrumentedDatabase implements Database {
})
}
checkSetup(): Promise<DocumentScope<any>> {
return tracer.trace("db.checkSetup", span => {
span?.addTags({ db_name: this.name })
return this.db.checkSetup()
})
}
get<T extends Document>(id?: string | undefined): Promise<T> {
return tracer.trace("db.get", span => {
span?.addTags({ db_name: this.name, doc_id: id })

View File

@ -121,7 +121,6 @@ export interface Database {
name: string
exists(): Promise<boolean>
checkSetup(): Promise<Nano.DocumentScope<any>>
get<T extends Document>(id?: string): Promise<T>
getMultiple<T extends Document>(
ids: string[],

778
yarn.lock

File diff suppressed because it is too large Load Diff