Improve server caching and ladder view refresh performance

This commit is contained in:
Nick Winter 2015-12-05 08:18:36 -08:00
parent b21c20c08d
commit 77731dad84
10 changed files with 48 additions and 37 deletions

View file

@ -149,6 +149,9 @@ module.exports = class LadderTabView extends CocoView
# LADDER LOADING
refreshLadder: ->
# Only do this so often if not in a league; servers cache a lot of this data for a few minutes anyway.
return if not @options.league and (new Date() - 2 * 60 * 1000 < @lastRefreshTime)
@lastRefreshTime = new Date()
@supermodel.resetProgress()
@ladderLimit ?= parseInt @getQueryVariable('top_players', if @options.league then 100 else 20)
for team in @teams
@ -171,7 +174,7 @@ module.exports = class LadderTabView extends CocoView
level = "#{@level.get('original')}.#{@level.get('version').major}"
url = "/db/level/#{level}/histogram_data?team=#{team.name.toLowerCase()}"
url += '&leagues.leagueID=' + @options.league.id if @options.league
$.get url, {cache: false}, (data) -> histogramData = data
$.get url, (data) -> histogramData = data
).then =>
@generateHistogram(histogramWrapper, histogramData, team.name.toLowerCase()) unless @destroyed
@ -311,6 +314,7 @@ module.exports = class LadderTabView extends CocoView
onLoadMoreLadderEntries: (e) ->
@ladderLimit ?= 100
@ladderLimit += 100
@lastRefreshTime = null
@refreshLadder()
module.exports.LeaderboardData = LeaderboardData = class LeaderboardData extends CocoClass

View file

@ -92,7 +92,14 @@ module.exports = class LadderView extends RootView
@insertSubView(@ladderTab = new LadderTabView({league: @league}, @level, @sessions))
@insertSubView(@myMatchesTab = new MyMatchesTabView({league: @league}, @level, @sessions))
@insertSubView(@simulateTab = new SimulateTabView(league: @league))
@refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), 60 * 1000)
highLoad = true
@refreshDelay = switch
when not application.isProduction() then 10 # Refresh very quickly in develompent.
when @league then 20 # Refresh quickly when looking at a league ladder.
when not highLoad then 30 # Refresh slowly when in production.
when not me.isAnonymous() then 60 # Refresh even more slowly during HoC scaling.
else 300 # Refresh super slowly if anonymous during HoC scaling.
@refreshInterval = setInterval(@fetchSessionsAndRefreshViews.bind(@), @refreshDelay * 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
@ -105,7 +112,7 @@ module.exports = class LadderView extends RootView
return if @destroyed or application.userIsIdle
@lastRefreshTime = new Date()
@ladderTab.refreshLadder()
@myMatchesTab.refreshMatches()
@myMatchesTab.refreshMatches @refreshDelay
@simulateTab.refresh()
onIdleChanged: (e) ->

View file

@ -14,9 +14,9 @@ module.exports = class MyMatchesTabView extends CocoView
super(options)
@nameMap = {}
@previouslyRankingTeams = {}
@refreshMatches()
@refreshMatches 20
refreshMatches: ->
refreshMatches: (@refreshDelay) ->
@teams = teamDataFromLevel @level
@loadNames()
@ -74,7 +74,7 @@ module.exports = class MyMatchesTabView extends CocoView
state = 'win'
state = 'loss' if match.metrics.rank > opponent.metrics.rank
state = 'tie' if match.metrics.rank is opponent.metrics.rank
fresh = match.date > (new Date(new Date() - 20 * 1000)).toISOString()
fresh = match.date > (new Date(new Date() - @refreshDelay * 1000)).toISOString()
if fresh
@playSound 'chat_received'
{

View file

@ -74,6 +74,7 @@ module.exports = class SimulateTabView extends CocoView
@simulator.fetchAndSimulateTask()
refresh: ->
return # Queue-based scoring is currently not active anyway, so don't keep checking this until we fix it.
success = (numberOfGamesInQueue) ->
$('#games-in-queue').text numberOfGamesInQueue
$.ajax '/queue/messagesInQueueCount', cache: false, success: success

View file

@ -69,7 +69,7 @@
"moment": "~2.5.0",
"mongodb": "^2.0.28",
"mongoose": "3.8.x",
"mongoose-cache": "~0.1.4",
"mongoose-cache": "https://github.com/nwinter/mongoose-cache/tarball/master",
"node-force-domain": "~0.1.0",
"node-gyp": "~0.13.0",
"node-statsd": "^0.1.1",

View file

@ -18,7 +18,8 @@ module.exports.connect = () ->
# https://github.com/LearnBoost/mongoose/issues/1910
Level = require '../levels/Level'
Aggregate = Level.aggregate().constructor
mongooseCache.install(mongoose, {max: 200, maxAge: 1 * 60 * 1000, debug: false}, Aggregate)
maxAge = (Math.random() * 10 + 10) * 60 * 1000 # Randomize so that each server doesn't refresh cache from db at same times
mongooseCache.install(mongoose, {max: 1000, maxAge: maxAge, debug: false}, Aggregate)
module.exports.generateMongoConnectionString = ->
if not testing and config.tokyo

View file

@ -208,7 +208,7 @@ LevelHandler = class LevelHandler extends Handler
{$match: match}
{$project: project}
]
aggregate.cache() unless league
aggregate.cache(10 * 60 * 1000) unless league
aggregate.exec (err, data) =>
if err? then return @sendDatabaseError res, err
@ -244,7 +244,7 @@ LevelHandler = class LevelHandler extends Handler
.limit(req.query.limit)
.sort(sortParameters)
.select(selectProperties.join ' ')
query.cache() if sessionsQueryParameters.totalScore.$lt is 1000000
query.cache(5 * 60 * 1000) if sessionsQueryParameters.totalScore.$lt is 1000000
query.exec (err, resultSessions) =>
return @sendDatabaseError(res, err) if err
@ -302,7 +302,7 @@ LevelHandler = class LevelHandler extends Handler
.find(level: leaderboardOptions.find.level, creator: {$in: @ladderBenchmarkAIs})
.sort(leaderboardOptions.sort)
.select(leaderboardOptions.select.join ' ')
.cache() # TODO: How long does this cache? We will probably want these to be pretty long.
.cache(30 * 60 * 1000)
.exec (err, aiSessions) ->
return cb err if err
matchingAISessions = _.filter aiSessions, (aiSession) ->
@ -350,9 +350,9 @@ LevelHandler = class LevelHandler extends Handler
findParameters['slug'] = slugOrID
selectString = 'original version'
query = Level.findOne(findParameters)
.select(selectString)
.lean()
.cache()
.select(selectString)
.lean()
.cache(60 * 60 * 1000)
query.exec (err, level) =>
return @sendDatabaseError(res, err) if err
@ -364,27 +364,25 @@ LevelHandler = class LevelHandler extends Handler
majorVersion: level.version.major
submitted: true
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
aggregate = Session.aggregate [
{$match: sessionQueryParams}
{$sort: {'totalScore': -1}}
{$limit: 20}
{$project: {'totalScore': 1}}
]
aggregate.cache()
aggregate.exec cb
teams = ['humans', 'ogres']
findTop20Players = (sessionQueryParams, team, cb) ->
sessionQueryParams['team'] = team
aggregate = Session.aggregate [
{$match: sessionQueryParams}
{$sort: {'totalScore': -1}}
{$limit: 20}
{$project: {'totalScore': 1}}
]
aggregate.cache(3 * 60 * 1000)
aggregate.exec cb
async.map teams, findTop20Players.bind(@, sessionsQueryParameters), (err, map) =>
if err? then return @sendDatabaseError(res, err)
sessions = []
for mapItem in map
sessions.push _.sample(mapItem)
if map.length != 2 then return @sendDatabaseError res, 'There aren\'t sessions of 2 teams, so cannot choose random opponents!'
@sendSuccess res, sessions
async.map teams, findTop20Players.bind(@, sessionsQueryParameters), (err, map) =>
if err? then return @sendDatabaseError(res, err)
sessions = []
for mapItem in map
sessions.push _.sample(mapItem)
if map.length != 2 then return @sendDatabaseError res, 'There aren\'t sessions of 2 teams, so cannot choose random opponents!'
@sendSuccess res, sessions
getFeedback: (req, res, levelID) ->
return @sendNotFoundError(res) unless req.user

View file

@ -71,7 +71,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
if limit? and limit < 1000
q.limit(limit)
q.cache()
q.cache(10 * 60 * 1000)
q.exec (err, documents) =>
return @sendDatabaseError(res, err) if err

View file

@ -55,7 +55,7 @@ class UserCodeProblemHandler extends Handler
group = {"$group": {"_id": {"errMessage": "$errMessageNoLineInfo", "errHint": "$errHint", "language": "$language", "levelID": "$levelID"}, "count": {"$sum": 1}}}
sort = { $sort : { "_id.levelID": 1, count : -1, "_id.language": 1 } }
query = UserCodeProblem.aggregate match, limit, group, sort
query.cache()
query.cache(30 * 60 * 1000)
query.exec (err, data) =>
if err? then return @sendDatabaseError res, err

View file

@ -199,7 +199,7 @@ 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.cache(10 * 60 * 1000) if req.query.scoreOffset is -1
leaderboardQuery.exec (err, otherUsers) ->
otherUsers = _.reject otherUsers, _id: req.user._id if req.query.scoreOffset isnt -1 and req.user
otherUsers ?= []