1
0
Fork 0
mirror of synced 2024-07-06 15:00:49 +12:00

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 { newid } from "../../docIds/newid"
import { DDInstrumentedDatabase } from "../instrumentation" import { DDInstrumentedDatabase } from "../instrumentation"
const DATABASE_NOT_FOUND = "Database does not exist."
function buildNano(couchInfo: { url: string; cookie: string }) { function buildNano(couchInfo: { url: string; cookie: string }) {
return Nano({ return Nano({
url: couchInfo.url, url: couchInfo.url,
@ -31,6 +33,8 @@ function buildNano(couchInfo: { url: string; cookie: string }) {
}) })
} }
type DBCall = () => Promise<any>
export function DatabaseWithConnection( export function DatabaseWithConnection(
dbName: string, dbName: string,
connection: string, connection: string,
@ -78,7 +82,11 @@ export class DatabaseImpl implements Database {
return this.instanceNano || DatabaseImpl.nano 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 let shouldCreate = !this.pouchOpts?.skip_setup
// check exists in a lightweight fashion // check exists in a lightweight fashion
let exists = await this.exists() 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 { try {
return await fnc() return await fnc()
} catch (err: any) { } 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 err.status = err.statusCode
} }
throw err throw err
@ -110,11 +126,12 @@ export class DatabaseImpl implements Database {
} }
async get<T extends Document>(id?: string): Promise<T> { async get<T extends Document>(id?: string): Promise<T> {
const db = await this.checkSetup() return this.performCall(db => {
if (!id) { if (!id) {
throw new Error("Unable to get doc without a valid _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>( async getMultiple<T extends Document>(
@ -147,22 +164,23 @@ export class DatabaseImpl implements Database {
} }
async remove(idOrDoc: string | Document, rev?: string) { async remove(idOrDoc: string | Document, rev?: string) {
const db = await this.checkSetup() return this.performCall(db => {
let _id: string let _id: string
let _rev: string let _rev: string
if (isDocument(idOrDoc)) { if (isDocument(idOrDoc)) {
_id = idOrDoc._id! _id = idOrDoc._id!
_rev = idOrDoc._rev! _rev = idOrDoc._rev!
} else { } else {
_id = idOrDoc _id = idOrDoc
_rev = rev! _rev = rev!
} }
if (!_id || !_rev) { if (!_id || !_rev) {
throw new Error("Unable to remove doc without a valid _id and _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) { async post(document: AnyDocument, opts?: DatabasePutOpts) {
@ -176,45 +194,49 @@ export class DatabaseImpl implements Database {
if (!document._id) { if (!document._id) {
throw new Error("Cannot store document without _id field.") throw new Error("Cannot store document without _id field.")
} }
const db = await this.checkSetup() return this.performCall(async db => {
if (!document.createdAt) { if (!document.createdAt) {
document.createdAt = new Date().toISOString() document.createdAt = new Date().toISOString()
} }
document.updatedAt = new Date().toISOString() document.updatedAt = new Date().toISOString()
if (opts?.force && document._id) { if (opts?.force && document._id) {
try { try {
const existing = await this.get(document._id) const existing = await this.get(document._id)
if (existing) { if (existing) {
document._rev = existing._rev document._rev = existing._rev
} }
} catch (err: any) { } catch (err: any) {
if (err.status !== 404) { if (err.status !== 404) {
throw err throw err
}
} }
} }
} return () => db.insert(document)
return this.updateOutput(() => db.insert(document)) })
} }
async bulkDocs(documents: AnyDocument[]) { async bulkDocs(documents: AnyDocument[]) {
const db = await this.checkSetup() return this.performCall(db => {
return this.updateOutput(() => db.bulk({ docs: documents })) return () => db.bulk({ docs: documents })
})
} }
async allDocs<T extends Document>( async allDocs<T extends Document>(
params: DatabaseQueryOpts params: DatabaseQueryOpts
): Promise<AllDocsResponse<T>> { ): Promise<AllDocsResponse<T>> {
const db = await this.checkSetup() return this.performCall(db => {
return this.updateOutput(() => db.list(params)) return () => db.list(params)
})
} }
async query<T extends Document>( async query<T extends Document>(
viewName: string, viewName: string,
params: DatabaseQueryOpts params: DatabaseQueryOpts
): Promise<AllDocsResponse<T>> { ): Promise<AllDocsResponse<T>> {
const db = await this.checkSetup() return this.performCall(db => {
const [database, view] = viewName.split("/") const [database, view] = viewName.split("/")
return this.updateOutput(() => db.view(database, view, params)) return () => db.view(database, view, params)
})
} }
async destroy() { async destroy() {
@ -231,8 +253,9 @@ export class DatabaseImpl implements Database {
} }
async compact() { async compact() {
const db = await this.checkSetup() return this.performCall(db => {
return this.updateOutput(() => db.compact()) return () => db.compact()
})
} }
// All below functions are in-frequently called, just utilise PouchDB // 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> { get<T extends Document>(id?: string | undefined): Promise<T> {
return tracer.trace("db.get", span => { return tracer.trace("db.get", span => {
span?.addTags({ db_name: this.name, doc_id: id }) span?.addTags({ db_name: this.name, doc_id: id })

View file

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

778
yarn.lock

File diff suppressed because it is too large Load diff