Add redis-based channel index

This commit is contained in:
calzoneman 2016-06-09 23:42:30 -07:00
parent 5b9948f709
commit b6bb0aa56d
6 changed files with 118 additions and 3 deletions

View File

@ -44,9 +44,9 @@
}, },
"scripts": { "scripts": {
"build-player": "$npm_node_execpath build-player.js", "build-player": "$npm_node_execpath build-player.js",
"build-server": "babel --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/", "build-server": "babel -D --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/",
"postinstall": "./postinstall.sh", "postinstall": "./postinstall.sh",
"server-dev": "babel --watch --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/" "server-dev": "babel -D --watch --source-maps --loose es6.destructuring,es6.forOf --out-dir lib/ src/"
}, },
"devDependencies": { "devDependencies": {
"coffee-script": "^1.9.2" "coffee-script": "^1.9.2"

View File

@ -0,0 +1,62 @@
import Promise from 'bluebird';
import uuid from 'uuid';
import { runLuaScript } from 'cytube-common/lib/redis/lualoader';
import path from 'path';
import Logger from '../logger';
var SERVER = null;
const CHANNEL_INDEX = 'publicChannelList';
const CACHE_REFRESH_INTERVAL = 30 * 1000;
const CACHE_EXPIRE_DELAY = 40 * 1000;
const READ_CHANNEL_LIST = path.join(__dirname, 'read_channel_list.lua')
class PartitionChannelIndex {
constructor(redisClient) {
this.redisClient = redisClient;
this.uid = uuid.v4();
this.cachedList = [];
process.nextTick(() => {
SERVER = require('../server').getServer();
this.refreshCache();
setInterval(this.refreshCache.bind(this), CACHE_REFRESH_INTERVAL);
});
}
refreshCache() {
this.publishLocalChannels();
runLuaScript(this.redisClient, READ_CHANNEL_LIST, [
0,
Date.now() - CACHE_EXPIRE_DELAY
]).then(result => {
this.cachedList = JSON.parse(result);
}).catch(error => {
Logger.errlog.log(`Failed to refresh channel list: ${error.stack}`);
});
}
publishLocalChannels() {
const channels = SERVER.packChannelList(true).map(channel => {
return {
name: channel.name,
mediatitle: channel.mediatitle,
pagetitle: channel.pagetitle,
usercount: channel.usercount
};
});
const entry = JSON.stringify({
timestamp: Date.now(),
channels
});
this.redisClient.hsetAsync(CHANNEL_INDEX, this.uid, entry).catch(error => {
Logger.errlog.log(`Failed to publish local channel list: ${error.stack}`);
});
}
listPublicChannels() {
return Promise.resolve(this.cachedList);
}
}
export { PartitionChannelIndex };

View File

@ -18,6 +18,10 @@ class PartitionConfig {
getIdentity() { getIdentity() {
return this.config.identity; return this.config.identity;
} }
getRedisConfig() {
return this.config.redis;
}
} }
export { PartitionConfig }; export { PartitionConfig };

View File

@ -2,6 +2,7 @@ import { loadFromToml } from 'cytube-common/lib/configuration/configloader';
import { PartitionConfig } from './partitionconfig'; import { PartitionConfig } from './partitionconfig';
import { PartitionDecider } from './partitiondecider'; import { PartitionDecider } from './partitiondecider';
import { PartitionClusterClient } from '../io/cluster/partitionclusterclient'; import { PartitionClusterClient } from '../io/cluster/partitionclusterclient';
import RedisClientProvider from 'cytube-common/lib/redis/redisclientprovider';
import logger from 'cytube-common/lib/logger'; import logger from 'cytube-common/lib/logger';
import LegacyConfig from '../config'; import LegacyConfig from '../config';
import path from 'path'; import path from 'path';
@ -58,6 +59,16 @@ class PartitionModule {
return this.partitionClusterClient; return this.partitionClusterClient;
} }
getRedisClientProvider() {
if (!this.redisClientProvider) {
this.redisClientProvider = new RedisClientProvider(
this.partitionConfig.getRedisConfig()
);
}
return this.redisClientProvider;
}
} }
export { PartitionModule }; export { PartitionModule };

View File

@ -0,0 +1,30 @@
local entries = redis.call('hgetall', 'publicChannelList')
if #entries == 0 then
return '[]'
end
local channelList = {}
-- ARGV[1] holds the expiration timestamp. Anything older than this
-- will be discarded.
local expiration = tonumber(ARGV[1])
for i = 1, #entries, 2 do
local uid = entries[i]
local entry = cjson.decode(entries[i+1])
local timestamp = tonumber(entry['timestamp'])
if timestamp < expiration then
redis.call('hdel', 'publicChannelList', uid)
else
local channels = entry['channels']
for j = 1, #channels do
channelList[#channelList+1] = channels[j]
end
end
end
-- Necessary to check for this condition because
-- if the table is empty, cjson will encode it as an object ('{}')
if #channelList == 0 then
return '[]'
else
return cjson.encode(channelList)
end

View File

@ -43,6 +43,7 @@ var db = require("./database");
var Flags = require("./flags"); var Flags = require("./flags");
var sio = require("socket.io"); var sio = require("socket.io");
import LocalChannelIndex from './web/localchannelindex'; import LocalChannelIndex from './web/localchannelindex';
import { PartitionChannelIndex } from './partition/partitionchannelindex';
import IOConfiguration from './configuration/ioconfig'; import IOConfiguration from './configuration/ioconfig';
import WebConfiguration from './configuration/webconfig'; import WebConfiguration from './configuration/webconfig';
import NullClusterClient from './io/cluster/nullclusterclient'; import NullClusterClient from './io/cluster/nullclusterclient';
@ -86,7 +87,14 @@ var Server = function () {
const ioConfig = IOConfiguration.fromOldConfig(Config); const ioConfig = IOConfiguration.fromOldConfig(Config);
const webConfig = WebConfiguration.fromOldConfig(Config); const webConfig = WebConfiguration.fromOldConfig(Config);
const clusterClient = initModule.getClusterClient(); const clusterClient = initModule.getClusterClient();
const channelIndex = new LocalChannelIndex(); var channelIndex;
if (Config.get("enable-partition")) {
channelIndex = new PartitionChannelIndex(
initModule.getRedisClientProvider().get()
);
} else {
channelIndex = new LocalChannelIndex();
}
self.express = express(); self.express = express();
require("./web/webserver").init(self.express, require("./web/webserver").init(self.express,
webConfig, webConfig,