Added one-minute in-memory server caching for a bunch of common queries.

This commit is contained in:
Nick Winter 2015-02-26 17:20:27 -08:00
parent 9b34d4e166
commit b4e9ee67f0
8 changed files with 51 additions and 39 deletions

View file

@ -64,7 +64,7 @@ module.exports = class LadderView extends RootView
@insertSubView(@ladderTab = new LadderTabView({}, @level, @sessions))
@insertSubView(@myMatchesTab = new MyMatchesTabView({}, @level, @sessions))
@insertSubView(@simulateTab = new SimulateTabView())
@refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 20 * 1000)
@refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 60 * 1000)
hash = document.location.hash[1..] if document.location.hash
if hash and not (hash in ['my-matches', 'simulate', 'ladder', 'prizes', 'rules', 'winners'])
@showPlayModal(hash) if @sessions.loaded

View file

@ -38,36 +38,37 @@
"multiplayer"
],
"dependencies": {
"express": "~3.0.6",
"winston": "0.6.x",
"passport": "0.1.x",
"passport-local": "0.1.x",
"moment": "~2.5.0",
"mongoose": "3.8.x",
"request": "2.12.x",
"tv4": "~1.0.16",
"lodash": "~2.4.1",
"underscore.string": "2.3.x",
"async": "0.2.x",
"connect": "2.7.x",
"coffee-script": "1.9.x",
"graceful-fs": "~2.0.1",
"node-force-domain": "~0.1.0",
"mailchimp-api": "2.0.x",
"express-useragent": "~0.0.9",
"gridfs-stream": "0.4.x",
"stream-buffers": "0.2.x",
"sendwithus": "2.1.x",
"aws-sdk": "~2.0.0",
"bayesian-battle": "0.0.x",
"redis": "",
"webworker-threads": "~0.5.5",
"node-gyp": "~0.13.0",
"aether": "~0.3.0",
"JASON": "~0.1.3",
"JQDeferred": "~2.1.0",
"aether": "~0.3.0",
"async": "0.2.x",
"aws-sdk": "~2.0.0",
"bayesian-battle": "0.0.x",
"coffee-script": "1.9.x",
"connect": "2.7.x",
"express": "~3.0.6",
"express-useragent": "~0.0.9",
"graceful-fs": "~2.0.1",
"gridfs-stream": "0.4.x",
"jsondiffpatch": "0.1.17",
"stripe": "~2.9.0"
"lodash": "~2.4.1",
"mailchimp-api": "2.0.x",
"moment": "~2.5.0",
"mongoose": "3.8.x",
"mongoose-cache": "https://github.com/nwinter/mongoose-cache/tarball/master",
"node-force-domain": "~0.1.0",
"node-gyp": "~0.13.0",
"passport": "0.1.x",
"passport-local": "0.1.x",
"redis": "",
"request": "2.12.x",
"sendwithus": "2.1.x",
"stream-buffers": "0.2.x",
"stripe": "~2.9.0",
"tv4": "~1.0.16",
"underscore.string": "2.3.x",
"webworker-threads": "~0.5.5",
"winston": "0.6.x"
},
"devDependencies": {
"auto-reload-brunch": "> 1.0 < 1.8",

View file

@ -2,6 +2,7 @@ config = require '../../server_config'
winston = require 'winston'
mongoose = require 'mongoose'
Grid = require 'gridfs-stream'
mongooseCache = require 'mongoose-cache'
global.testing = testing = '--unittest' in process.argv
@ -13,6 +14,7 @@ module.exports.connect = () ->
mongoose.connect address
mongoose.connection.once 'open', -> Grid.gfs = Grid(mongoose.connection.db, mongoose.mongo)
mongooseCache.install(mongoose, {max: 200, maxAge: 1 * 60 * 1000, debug: false})
module.exports.generateMongoConnectionString = ->
if not testing and config.mongo.mongoose_replica_string

View file

@ -164,6 +164,7 @@ LevelHandler = class LevelHandler extends Handler
{$match: {'levelID': slug, 'submitted': true, 'team': req.query.team}}
{$project: {totalScore: 1, _id: 0}}
]
#query.cache() # TODO: implement caching for aggregates
query.exec (err, data) =>
if err? then return @sendDatabaseError res, err
@ -199,6 +200,7 @@ LevelHandler = class LevelHandler extends Handler
.limit(req.query.limit)
.sort(sortParameters)
.select(selectProperties.join ' ')
query.cache() if sessionsQueryParameters.totalScore.$lt is 1000000
query.exec (err, resultSessions) =>
return @sendDatabaseError(res, err) if err
@ -269,6 +271,7 @@ LevelHandler = class LevelHandler extends Handler
query = Level.findOne(findParameters)
.select(selectString)
.lean()
.cache()
query.exec (err, level) =>
return @sendDatabaseError(res, err) if err
@ -280,17 +283,19 @@ LevelHandler = class LevelHandler extends Handler
majorVersion: level.version.major
submitted: true
query = Session.find(sessionsQueryParameters).distinct('team')
query = Session.find(sessionsQueryParameters).distinct('team').cache()
query.exec (err, teams) =>
return @sendDatabaseError res, err if err? or not teams
findTop20Players = (sessionQueryParams, team, cb) ->
sessionQueryParams['team'] = team
Session.aggregate [
aggregate = Session.aggregate [
{$match: sessionQueryParams}
{$project: {'totalScore': 1}}
{$sort: {'totalScore': -1}}
{$limit: 20}
], cb
]
#aggregate.cache() # TODO: implement caching for aggregates
aggregate.exec cb
async.map teams, findTop20Players.bind(@, sessionsQueryParameters), (err, map) =>
if err? then return @sendDatabaseError(res, err)

View file

@ -70,6 +70,8 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
if limit? and limit < 1000
q.limit(limit)
q.cache()
q.exec (err, documents) =>
return @sendDatabaseError(res, err) if err
documents = (@formatEntity(req, doc) for doc in documents)

View file

@ -138,7 +138,7 @@ module.exports.getTwoGames = (req, res) ->
'submitted': true
'team': 'humans'
selection = 'team totalScore transpiledCode submittedCodeLanguage teamSpells levelID creatorName creator submitDate'
LevelSession.count queryParams, (err, numberOfHumans) =>
LevelSession.count(queryParams).cache().exec (err, numberOfHumans) =>
if err? then return errors.serverError(res, 'Couldn\'t get the number of human games')
unless numberOfHumans
res.send(204, 'No games to score.')
@ -149,7 +149,7 @@ module.exports.getTwoGames = (req, res) ->
'levelID': levelID
'submitted': true
'team': 'ogres'
LevelSession.count ogreCountParams, (err, numberOfOgres) =>
LevelSession.count(ogreCountParams).cache().exec (err, numberOfOgres) =>
if err? then return errors.serverError(res, 'Couldn\'t get the number of ogre games')
unless numberOfOgres
res.send(204, 'No games to score.')

View file

@ -172,11 +172,12 @@ UserHandler = class UserHandler extends Handler
getSimulatorLeaderboard: (req, res) ->
queryParameters = @getSimulatorLeaderboardQueryParameters(req)
leaderboardQuery = User.find(queryParameters.query).select('name simulatedBy simulatedFor').sort({'simulatedBy': queryParameters.sortOrder}).limit(queryParameters.limit)
leaderboardQuery.cache() if req.query.scoreOffset is -1
leaderboardQuery.exec (err, otherUsers) ->
otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1
otherUsers ?= []
res.send(otherUsers)
res.end()
otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1
otherUsers ?= []
res.send(otherUsers)
res.end()
getMySimulatorLeaderboardRank: (req, res) ->
req.query.order = 1
@ -229,12 +230,12 @@ UserHandler = class UserHandler extends Handler
for prop, val of obj
user.set(prop, undefined) unless prop is '_id'
user.set('deleted', true)
# Hack to get saving of Users to work. Probably should replace these props with strings
# so that validation doesn't get hung up on Date objects in the documents.
delete obj.dateCreated
user.save (err) =>
user.save (err) =>
return @sendDatabaseError(res, err) if err
@sendNoContent res

View file

@ -53,6 +53,7 @@ setupErrorMiddleware = (app) ->
hipchat.sendHipChatMessage(message, ['tower'], {papertrail: true})
else
next(err)
setupExpressMiddleware = (app) ->
if config.isProduction
express.logger.format('prod', productionLogging)