Browse Source

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).

master
mike12345567 5 years ago
parent
commit
7eb29ffc7d
  1. 1
      packages/auth/cache.js
  2. 35
      packages/auth/src/cache/appMetadata.js
  3. 4
      packages/auth/src/db/index.js
  4. 3
      packages/auth/src/db/utils.js
  5. 10
      packages/auth/src/redis/authRedis.js
  6. 1
      packages/auth/src/redis/utils.js
  7. 7
      packages/server/src/api/controllers/application.js
  8. 2
      packages/server/src/api/controllers/deploy/index.js
  9. 2
      packages/server/src/api/controllers/dev.js
  10. 2
      packages/server/src/middleware/builder.js
  11. 1
      packages/server/src/utilities/index.js

1
packages/auth/cache.js

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

35
packages/auth/src/cache/appMetadata.js

@ -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
packages/auth/src/db/index.js

@ -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 = () => {

3
packages/auth/src/db/utils.js

@ -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 []

10
packages/auth/src/redis/authRedis.js

@ -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
},
} }

1
packages/auth/src/redis/utils.js

@ -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

7
packages/server/src/api/controllers/application.js

@ -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) => {

2
packages/server/src/api/controllers/deploy/index.js

@ -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")

2
packages/server/src/api/controllers/dev.js

@ -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.",
} }

2
packages/server/src/middleware/builder.js

@ -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)
} }

1
packages/server/src/utilities/index.js

@ -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…
Cancel
Save