Updating the getAllApps function to use a cached version of the app metadata, rather than retrieving it individually everytime. Also invalidating the results everytime they are updated (at least in the important locations).

This commit is contained in:
mike12345567 2021-11-15 17:40:45 +00:00
parent 9b5909e367
commit 7eb29ffc7d
11 changed files with 63 additions and 5 deletions

View File

@ -1,3 +1,4 @@
module.exports = {
user: require("./src/cache/user"),
app: require("./src/cache/appMetadata"),
}

35
packages/auth/src/cache/appMetadata.js vendored Normal file
View File

@ -0,0 +1,35 @@
const redis = require("../redis/authRedis")
const { getDB } = require("../db")
const { DocumentTypes } = require("../db/constants")
const EXPIRY_SECONDS = 3600
/**
* The default populate app metadata function
*/
const populateFromDB = async appId => {
return getDB(appId, { skip_setup: true }).get(DocumentTypes.APP_METADATA)
}
/**
* Get the requested app metadata by id.
* Use redis cache to first read the app metadata.
* If not present fallback to loading the app metadata directly and re-caching.
* @param {*} appId the id of the app to get metadata from.
* @returns {object} the app metadata.
*/
exports.getAppMetadata = async appId => {
const client = await redis.getAppClient()
// try cache
let metadata = await client.get(appId)
if (!metadata) {
metadata = await populateFromDB(appId)
client.store(appId, metadata, EXPIRY_SECONDS)
}
return metadata
}
exports.invalidateAppMetadata = async appId => {
const client = await redis.getAppClient()
await client.delete(appId)
}

View File

@ -4,8 +4,8 @@ module.exports.setDB = pouch => {
Pouch = pouch
}
module.exports.getDB = dbName => {
return new Pouch(dbName)
module.exports.getDB = (dbName, opts = {}) => {
return new Pouch(dbName, opts)
}
module.exports.getCouch = () => {

View File

@ -6,6 +6,7 @@ const { StaticDatabases, SEPARATOR, DocumentTypes } = require("./constants")
const { getTenantId, getTenantIDFromAppID } = require("../tenancy")
const fetch = require("node-fetch")
const { getCouch } = require("./index")
const { getAppMetadata } = require("../cache/appMetadata")
const UNICODE_MAX = "\ufff0"
@ -234,7 +235,7 @@ exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => {
}
const appPromises = appDbNames.map(db =>
// skip setup otherwise databases could be re-created
new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA)
getAppMetadata(db)
)
if (appPromises.length === 0) {
return []

View File

@ -1,16 +1,18 @@
const Client = require("./index")
const utils = require("./utils")
let userClient, sessionClient
let userClient, sessionClient, appClient
async function init() {
userClient = await new Client(utils.Databases.USER_CACHE).init()
sessionClient = await new Client(utils.Databases.SESSIONS).init()
appClient = await new Client(utils.Databases.APP_METADATA).init()
}
process.on("exit", async () => {
if (userClient) await userClient.finish()
if (sessionClient) await sessionClient.finish()
if (appClient) await appClient.finish()
})
module.exports = {
@ -26,4 +28,10 @@ module.exports = {
}
return sessionClient
},
getAppClient: async () => {
if (!appClient) {
await init()
}
return appClient
},
}

View File

@ -15,6 +15,7 @@ exports.Databases = {
SESSIONS: "session",
USER_CACHE: "users",
FLAGS: "flags",
APP_METADATA: "appMetadata",
}
exports.SEPARATOR = SEPARATOR

View File

@ -45,6 +45,7 @@ const {
} = require("../../utilities/fileSystem/clientLibrary")
const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy")
const { syncGlobalUsers } = require("./user")
const { app: appCache } = require("@budibase/auth/cache")
const URL_REGEX_SLASH = /\/|\\/g
@ -319,6 +320,7 @@ exports.delete = async ctx => {
}
// make sure the app/role doesn't stick around after the app has been deleted
await removeAppFromUserRoles(ctx, ctx.params.appId)
await appCache.invalidateAppMetadata(ctx.params.appId)
ctx.status = 200
ctx.body = result
@ -387,7 +389,10 @@ const updateAppPackage = async (ctx, appPackage, appId) => {
// Redis, shouldn't ever store it
delete newAppPackage.lockedBy
return await db.put(newAppPackage)
const response = await db.put(newAppPackage)
// remove any cached metadata, so that it will be updated
await appCache.invalidateAppMetadata(appId)
return response
}
const createEmptyAppPackage = async (ctx, app) => {

View File

@ -6,6 +6,7 @@ const {
disableAllCrons,
enableCronTrigger,
} = require("../../../automations/utils")
const { app: appCache } = require("@budibase/auth/cache")
// the max time we can wait for an invalidation to complete before considering it failed
const MAX_PENDING_TIME_MS = 30 * 60000
@ -103,6 +104,7 @@ async function deployApp(deployment) {
appDoc.appId = productionAppId
appDoc.instance._id = productionAppId
await db.put(appDoc)
await appCache.invalidateAppMetadata(productionAppId)
console.log("New app doc written successfully.")
await initDeployedApp(productionAppId)
console.log("Deployed app initialised, setting deployment to successful")

View File

@ -6,6 +6,7 @@ const { request } = require("../../utilities/workerRequests")
const { clearLock } = require("../../utilities/redis")
const { Replication } = require("@budibase/auth").db
const { DocumentTypes } = require("../../db/utils")
const { app: appCache } = require("@budibase/auth/cache")
async function redirect(ctx, method, path = "global") {
const { devPath } = ctx.params
@ -106,6 +107,7 @@ exports.revert = async ctx => {
appDoc.appId = appId
appDoc.instance._id = appId
await db.put(appDoc)
await appCache.invalidateAppMetadata(appId)
ctx.body = {
message: "Reverted changes successfully.",
}

View File

@ -8,6 +8,7 @@ const {
const CouchDB = require("../db")
const { DocumentTypes } = require("../db/utils")
const { PermissionTypes } = require("@budibase/auth/permissions")
const { app: appCache } = require("@budibase/auth/cache")
const DEBOUNCE_TIME_SEC = 30
@ -51,6 +52,7 @@ async function updateAppUpdatedAt(ctx) {
const metadata = await db.get(DocumentTypes.APP_METADATA)
metadata.updatedAt = new Date().toISOString()
await db.put(metadata)
await appCache.invalidateAppMetadata(appId)
// set a new debounce record with a short TTL
await setDebounce(appId, DEBOUNCE_TIME_SEC)
}

View File

@ -48,6 +48,7 @@ exports.objectStoreUrl = () => {
* via a specific endpoint (under /api/assets/client).
* @param {string} appId In production we need the appId to look up the correct bucket, as the
* version of the client lib may differ between apps.
* @param {string} version The version to retrieve.
* @return {string} The URL to be inserted into appPackage response or server rendered
* app index file.
*/