1
0
Fork 0
mirror of synced 2024-07-04 22:11:23 +12:00

Merge pull request #12765 from Budibase/fix/remove-db-head-requests

Remove constant CouchDB HEAD requests
This commit is contained in:
Michael Drury 2024-01-11 17:23:26 +00:00 committed by GitHub
commit 4da2b0e361
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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<T> = () => Promise<T>
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<T>(
call: (db: Nano.DocumentScope<any>) => Promise<DBCall<T>> | DBCall<T>
): 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()
if (!id) {
throw new Error("Unable to get doc without a valid _id.")
}
return this.updateOutput(() => db.get(id))
return this.performCall(db => {
if (!id) {
throw new Error("Unable to get doc without a valid _id.")
}
return () => db.get(id)
})
}
async getMultiple<T extends Document>(
@ -147,22 +164,23 @@ export class DatabaseImpl implements Database {
}
async remove(idOrDoc: string | Document, rev?: string) {
const db = await this.checkSetup()
let _id: string
let _rev: string
return this.performCall(db => {
let _id: string
let _rev: string
if (isDocument(idOrDoc)) {
_id = idOrDoc._id!
_rev = idOrDoc._rev!
} else {
_id = idOrDoc
_rev = rev!
}
if (isDocument(idOrDoc)) {
_id = idOrDoc._id!
_rev = idOrDoc._rev!
} else {
_id = idOrDoc
_rev = rev!
}
if (!_id || !_rev) {
throw new Error("Unable to remove doc without a valid _id and _rev.")
}
return this.updateOutput(() => db.destroy(_id, _rev))
if (!_id || !_rev) {
throw new Error("Unable to remove doc without a valid _id and _rev.")
}
return () => db.destroy(_id, _rev)
})
}
async post(document: AnyDocument, opts?: DatabasePutOpts) {
@ -176,45 +194,49 @@ export class DatabaseImpl implements Database {
if (!document._id) {
throw new Error("Cannot store document without _id field.")
}
const db = await this.checkSetup()
if (!document.createdAt) {
document.createdAt = new Date().toISOString()
}
document.updatedAt = new Date().toISOString()
if (opts?.force && document._id) {
try {
const existing = await this.get(document._id)
if (existing) {
document._rev = existing._rev
}
} catch (err: any) {
if (err.status !== 404) {
throw err
return this.performCall(async db => {
if (!document.createdAt) {
document.createdAt = new Date().toISOString()
}
document.updatedAt = new Date().toISOString()
if (opts?.force && document._id) {
try {
const existing = await this.get(document._id)
if (existing) {
document._rev = existing._rev
}
} catch (err: any) {
if (err.status !== 404) {
throw err
}
}
}
}
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()
const [database, view] = viewName.split("/")
return this.updateOutput(() => db.view(database, view, params))
return this.performCall(db => {
const [database, view] = viewName.split("/")
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