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:
parent
0f54787072
commit
f13257bebe
|
@ -1,3 +1,4 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
user: require("./src/cache/user"),
|
user: require("./src/cache/user"),
|
||||||
|
app: require("./src/cache/appMetadata"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -4,8 +4,8 @@ module.exports.setDB = pouch => {
|
||||||
Pouch = pouch
|
Pouch = pouch
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getDB = dbName => {
|
module.exports.getDB = (dbName, opts = {}) => {
|
||||||
return new Pouch(dbName)
|
return new Pouch(dbName, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getCouch = () => {
|
module.exports.getCouch = () => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ const { StaticDatabases, SEPARATOR, DocumentTypes } = require("./constants")
|
||||||
const { getTenantId, getTenantIDFromAppID } = require("../tenancy")
|
const { getTenantId, getTenantIDFromAppID } = require("../tenancy")
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const { getCouch } = require("./index")
|
const { getCouch } = require("./index")
|
||||||
|
const { getAppMetadata } = require("../cache/appMetadata")
|
||||||
|
|
||||||
const UNICODE_MAX = "\ufff0"
|
const UNICODE_MAX = "\ufff0"
|
||||||
|
|
||||||
|
@ -234,7 +235,7 @@ exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => {
|
||||||
}
|
}
|
||||||
const appPromises = appDbNames.map(db =>
|
const appPromises = appDbNames.map(db =>
|
||||||
// skip setup otherwise databases could be re-created
|
// skip setup otherwise databases could be re-created
|
||||||
new CouchDB(db, { skip_setup: true }).get(DocumentTypes.APP_METADATA)
|
getAppMetadata(db)
|
||||||
)
|
)
|
||||||
if (appPromises.length === 0) {
|
if (appPromises.length === 0) {
|
||||||
return []
|
return []
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
const Client = require("./index")
|
const Client = require("./index")
|
||||||
const utils = require("./utils")
|
const utils = require("./utils")
|
||||||
|
|
||||||
let userClient, sessionClient
|
let userClient, sessionClient, appClient
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
userClient = await new Client(utils.Databases.USER_CACHE).init()
|
userClient = await new Client(utils.Databases.USER_CACHE).init()
|
||||||
sessionClient = await new Client(utils.Databases.SESSIONS).init()
|
sessionClient = await new Client(utils.Databases.SESSIONS).init()
|
||||||
|
appClient = await new Client(utils.Databases.APP_METADATA).init()
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on("exit", async () => {
|
process.on("exit", async () => {
|
||||||
if (userClient) await userClient.finish()
|
if (userClient) await userClient.finish()
|
||||||
if (sessionClient) await sessionClient.finish()
|
if (sessionClient) await sessionClient.finish()
|
||||||
|
if (appClient) await appClient.finish()
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -26,4 +28,10 @@ module.exports = {
|
||||||
}
|
}
|
||||||
return sessionClient
|
return sessionClient
|
||||||
},
|
},
|
||||||
|
getAppClient: async () => {
|
||||||
|
if (!appClient) {
|
||||||
|
await init()
|
||||||
|
}
|
||||||
|
return appClient
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ exports.Databases = {
|
||||||
SESSIONS: "session",
|
SESSIONS: "session",
|
||||||
USER_CACHE: "users",
|
USER_CACHE: "users",
|
||||||
FLAGS: "flags",
|
FLAGS: "flags",
|
||||||
|
APP_METADATA: "appMetadata",
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.SEPARATOR = SEPARATOR
|
exports.SEPARATOR = SEPARATOR
|
||||||
|
|
|
@ -45,6 +45,7 @@ const {
|
||||||
} = require("../../utilities/fileSystem/clientLibrary")
|
} = require("../../utilities/fileSystem/clientLibrary")
|
||||||
const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy")
|
const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy")
|
||||||
const { syncGlobalUsers } = require("./user")
|
const { syncGlobalUsers } = require("./user")
|
||||||
|
const { app: appCache } = require("@budibase/auth/cache")
|
||||||
|
|
||||||
const URL_REGEX_SLASH = /\/|\\/g
|
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
|
// make sure the app/role doesn't stick around after the app has been deleted
|
||||||
await removeAppFromUserRoles(ctx, ctx.params.appId)
|
await removeAppFromUserRoles(ctx, ctx.params.appId)
|
||||||
|
await appCache.invalidateAppMetadata(ctx.params.appId)
|
||||||
|
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.body = result
|
ctx.body = result
|
||||||
|
@ -387,7 +389,10 @@ const updateAppPackage = async (ctx, appPackage, appId) => {
|
||||||
// Redis, shouldn't ever store it
|
// Redis, shouldn't ever store it
|
||||||
delete newAppPackage.lockedBy
|
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) => {
|
const createEmptyAppPackage = async (ctx, app) => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ const {
|
||||||
disableAllCrons,
|
disableAllCrons,
|
||||||
enableCronTrigger,
|
enableCronTrigger,
|
||||||
} = require("../../../automations/utils")
|
} = 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
|
// the max time we can wait for an invalidation to complete before considering it failed
|
||||||
const MAX_PENDING_TIME_MS = 30 * 60000
|
const MAX_PENDING_TIME_MS = 30 * 60000
|
||||||
|
@ -103,6 +104,7 @@ async function deployApp(deployment) {
|
||||||
appDoc.appId = productionAppId
|
appDoc.appId = productionAppId
|
||||||
appDoc.instance._id = productionAppId
|
appDoc.instance._id = productionAppId
|
||||||
await db.put(appDoc)
|
await db.put(appDoc)
|
||||||
|
await appCache.invalidateAppMetadata(productionAppId)
|
||||||
console.log("New app doc written successfully.")
|
console.log("New app doc written successfully.")
|
||||||
await initDeployedApp(productionAppId)
|
await initDeployedApp(productionAppId)
|
||||||
console.log("Deployed app initialised, setting deployment to successful")
|
console.log("Deployed app initialised, setting deployment to successful")
|
||||||
|
|
|
@ -6,6 +6,7 @@ const { request } = require("../../utilities/workerRequests")
|
||||||
const { clearLock } = require("../../utilities/redis")
|
const { clearLock } = require("../../utilities/redis")
|
||||||
const { Replication } = require("@budibase/auth").db
|
const { Replication } = require("@budibase/auth").db
|
||||||
const { DocumentTypes } = require("../../db/utils")
|
const { DocumentTypes } = require("../../db/utils")
|
||||||
|
const { app: appCache } = require("@budibase/auth/cache")
|
||||||
|
|
||||||
async function redirect(ctx, method, path = "global") {
|
async function redirect(ctx, method, path = "global") {
|
||||||
const { devPath } = ctx.params
|
const { devPath } = ctx.params
|
||||||
|
@ -106,6 +107,7 @@ exports.revert = async ctx => {
|
||||||
appDoc.appId = appId
|
appDoc.appId = appId
|
||||||
appDoc.instance._id = appId
|
appDoc.instance._id = appId
|
||||||
await db.put(appDoc)
|
await db.put(appDoc)
|
||||||
|
await appCache.invalidateAppMetadata(appId)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: "Reverted changes successfully.",
|
message: "Reverted changes successfully.",
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ const {
|
||||||
const CouchDB = require("../db")
|
const CouchDB = require("../db")
|
||||||
const { DocumentTypes } = require("../db/utils")
|
const { DocumentTypes } = require("../db/utils")
|
||||||
const { PermissionTypes } = require("@budibase/auth/permissions")
|
const { PermissionTypes } = require("@budibase/auth/permissions")
|
||||||
|
const { app: appCache } = require("@budibase/auth/cache")
|
||||||
|
|
||||||
const DEBOUNCE_TIME_SEC = 30
|
const DEBOUNCE_TIME_SEC = 30
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ async function updateAppUpdatedAt(ctx) {
|
||||||
const metadata = await db.get(DocumentTypes.APP_METADATA)
|
const metadata = await db.get(DocumentTypes.APP_METADATA)
|
||||||
metadata.updatedAt = new Date().toISOString()
|
metadata.updatedAt = new Date().toISOString()
|
||||||
await db.put(metadata)
|
await db.put(metadata)
|
||||||
|
await appCache.invalidateAppMetadata(appId)
|
||||||
// set a new debounce record with a short TTL
|
// set a new debounce record with a short TTL
|
||||||
await setDebounce(appId, DEBOUNCE_TIME_SEC)
|
await setDebounce(appId, DEBOUNCE_TIME_SEC)
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ exports.objectStoreUrl = () => {
|
||||||
* via a specific endpoint (under /api/assets/client).
|
* 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
|
* @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.
|
* 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
|
* @return {string} The URL to be inserted into appPackage response or server rendered
|
||||||
* app index file.
|
* app index file.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue