Add support for caching Slack profile data in production using Memcached

This commit is contained in:
Pedro Pombeiro 2018-03-29 18:28:04 +02:00
parent 000977a2c1
commit 8c701a61fa
No known key found for this signature in database
GPG Key ID: A65DEB11E4BBC647
4 changed files with 868 additions and 304 deletions

6
.vscode/launch.json vendored
View File

@ -24,7 +24,11 @@
"${workspaceFolder}/index.js" "${workspaceFolder}/index.js"
], ],
"env": { "env": {
"DEBUG": "true" "DEBUG": "true",
"KUDOS_BOT_CONFIG": "{'options':{'process_whole_history':true},'slack':{'channel_id':'C8P4F6WTB'},'rules':{'tip_per_kudo_in_usd':0.02,'tip_per_reaction_in_usd':0.001,'reaction_threshold':3},'payments':{'STT':{'network_id':'ropsten','private_key':'0xaaaa','contract_address':'0xc55cF4B03948D7EBc8b9E8BAD92643703811d162'},'SNT':{'network_id':'homestead','private_key':'0xaaaa','contract_address':'0x744d70FDBE2Ba4CF95131626614a1763DF805B9E'}}}",
"MEMCACHE_URL": "",
"MEMCACHE_USERNAME": "",
"MEMCACHE_PASSWORD": ""
} }
}, },
{ {

View File

@ -9,13 +9,23 @@
// PombeirP // PombeirP
const MemCache = require('mem-cache') const MemCache = require('mem-cache')
const fs = require('fs') const memjs = require('memjs')
const { WebClient } = require('@slack/client') const { WebClient } = require('@slack/client')
const token = process.env.SLACK_USER_TOKEN || '' const token = process.env.SLACK_USER_TOKEN || ''
const cachePath = './slack-cache.json' const cacheMemcachedKey = 'slack-profile-cache-json'
var allowLoadFromCache = true var allowLoadFromCache = true
// Environment variables are defined in app.yaml.
let MEMCACHE_URL = process.env.MEMCACHE_URL || '127.0.0.1:11211'
if (process.env.USE_GAE_MEMCACHE) {
MEMCACHE_URL = `${process.env.GAE_MEMCACHE_HOST}:${process.env.GAE_MEMCACHE_PORT}`
}
const mc = memjs.Client.create(MEMCACHE_URL, {
username: process.env.MEMCACHE_USERNAME,
password: process.env.MEMCACHE_PASSWORD
})
module.exports = (robot) => new GitHubSlackIdMapper(robot) module.exports = (robot) => new GitHubSlackIdMapper(robot)
class GitHubSlackIdMapper { class GitHubSlackIdMapper {
@ -28,6 +38,15 @@ class GitHubSlackIdMapper {
setInterval(() => internalBuild(this.robot, this.cache), 24 * 60 * 60 * 1000) setInterval(() => internalBuild(this.robot, this.cache), 24 * 60 * 60 * 1000)
} }
async getSlackUsernameFromSlackId (slackUserId) {
await this.buildPromise
const profile = this.cache.get(getSlackId2ProfileCacheKeyName(slackUserId))
if (profile) {
return profile.name
}
return null
}
async getGitHubHandleFromSlackId (slackUserId) { async getGitHubHandleFromSlackId (slackUserId) {
await this.buildPromise await this.buildPromise
const profile = this.cache.get(getSlackId2ProfileCacheKeyName(slackUserId)) const profile = this.cache.get(getSlackId2ProfileCacheKeyName(slackUserId))
@ -54,13 +73,13 @@ class GitHubSlackIdMapper {
async function internalBuild (robot, cache) { async function internalBuild (robot, cache) {
if (allowLoadFromCache) { if (allowLoadFromCache) {
try { try {
const json = fs.readFileSync(cachePath) const json = await mc.get(cacheMemcachedKey)
if (json) { if (json.value) {
const cacheFromFile = JSON.parse(json) const cacheFromFile = JSON.parse(json.value)
for (const kvp of cacheFromFile) { for (const kvp of cacheFromFile) {
cache.set(kvp.k, kvp.v) cache.set(kvp.k, kvp.v)
} }
robot.log.info(`Read Slack user cache from file (${cache.length} entries)`) robot.log.info(`Read Slack user cache from ${MEMCACHE_URL} (${cache.length} entries)`)
allowLoadFromCache = false allowLoadFromCache = false
return return
} }
@ -100,14 +119,14 @@ async function internalBuild (robot, cache) {
robot.log.warn(`No GitHub ID field found in @${username} (${user.id}) profile!`) robot.log.warn(`No GitHub ID field found in @${username} (${user.id}) profile!`)
} }
const gitHubUsername = gitHubFieldId && profile.fields && profile.fields[gitHubFieldId] ? profile.fields[gitHubFieldId].value : null const gitHubUsername = gitHubFieldId && profile.fields && profile.fields[gitHubFieldId] ? profile.fields[gitHubFieldId].value.replace('https://github.com/', '') : null
if (gitHubUsername) { if (gitHubUsername) {
usersContainingGitHubInfo = usersContainingGitHubInfo.concat(username) usersContainingGitHubInfo = usersContainingGitHubInfo.concat(username)
} else { } else {
usersMissingGitHubInfo = usersMissingGitHubInfo.concat(username) usersMissingGitHubInfo = usersMissingGitHubInfo.concat(username)
} }
const data = { github_handle: gitHubUsername } const data = { name: username, github_handle: gitHubUsername }
robot.log.debug(`@${username} (${user.id}) -> ${JSON.stringify(data)}`) robot.log.debug(`@${username} (${user.id}) -> ${JSON.stringify(data)}`)
@ -144,7 +163,15 @@ async function internalBuild (robot, cache) {
for (const key of cache.keys) { for (const key of cache.keys) {
c.push({ k: key, v: cache.get(key) }) c.push({ k: key, v: cache.get(key) })
} }
fs.writeFileSync(cachePath, JSON.stringify(c, null, 2))
if (mc) {
try {
await mc.set(cacheMemcachedKey, c, {})
robot.log.info(`Saved cache to Memcached`)
} catch (error) {
robot.log.warn(`Error while saving cache to Memcached: ${error}`)
}
}
} catch (e) { } catch (e) {
robot.log.error(`Error while populating Slack user ID cache: ${e}`) robot.log.error(`Error while populating Slack user ID cache: ${e}`)
} }

1120
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"hashset": "0.0.6", "hashset": "0.0.6",
"jenkins": "^0.20.1", "jenkins": "^0.20.1",
"mem-cache": "0.0.5", "mem-cache": "0.0.5",
"memjs": "^1.2.0",
"probot": "^5.0.0", "probot": "^5.0.0",
"probot-config": "^0.1.0", "probot-config": "^0.1.0",
"probot-gpg-status": "^0.5.4", "probot-gpg-status": "^0.5.4",