
When user has Travis CI's web page open in the background we should not process pusher events immediately, because she will not see the changes on a page anyway. During peak hours we can get several messages per second, which requires some CPU work almost all the time. By using visibility API we can detect if the tab with Travis CI is in the background and if that's the case we will process pusher messages in batches every 5 minutes. This is much better for the CPU, because it means one bigger spike every few minutes and additionally some of the updates don't have to be done (for example if we get several job:started events, Ember's run loop will process only last one when it comes to rendering 'current' build).
93 lines
2.9 KiB
CoffeeScript
93 lines
2.9 KiB
CoffeeScript
Travis.Pusher = (key) ->
|
|
@init(key) # if key
|
|
this
|
|
|
|
$.extend Travis.Pusher,
|
|
CHANNELS: ['common']
|
|
CHANNEL_PREFIX: ''
|
|
ENCRYPTED: false
|
|
|
|
$.extend Travis.Pusher.prototype,
|
|
active_channels: []
|
|
|
|
init: (key) ->
|
|
Pusher.warn = @warn.bind(this)
|
|
@pusher = new Pusher(key, encrypted: Travis.Pusher.ENCRYPTED)
|
|
@subscribeAll(Travis.Pusher.CHANNELS) if Travis.Pusher.CHANNELS
|
|
|
|
@callbacksToProcess = []
|
|
|
|
Visibility.change (e, state) =>
|
|
if state == 'visible'
|
|
@processSavedCallbacks()
|
|
|
|
setInterval @processSavedCallbacks.bind(this), @processingIntervalWhenHidden
|
|
|
|
subscribeAll: (channels) ->
|
|
for channel in channels
|
|
name = @prefix(channel)
|
|
unless @pusher.channels.find(name)
|
|
channel = @pusher.channels.add(name, this)
|
|
channel.bind_all((event, data) => @receive(event, data))
|
|
@pusher.subscribeAll()
|
|
|
|
subscribe: (channel) ->
|
|
console.log("subscribing to #{channel}")
|
|
channel = @prefix(channel)
|
|
@pusher.subscribe(channel).bind_all((event, data) => @receive(event, data)) unless @pusher?.channel(channel)
|
|
|
|
unsubscribe: (channel) ->
|
|
console.log("unsubscribing from #{channel}")
|
|
channel = @prefix(channel)
|
|
@pusher.unsubscribe(channel) if @pusher?.channel(channel)
|
|
|
|
prefix: (channel) ->
|
|
"#{Travis.Pusher.CHANNEL_PREFIX}#{channel}"
|
|
|
|
# process pusher messages in batches every 5 minutes when the page is hidden
|
|
processingIntervalWhenHidden: 1000 * 60 * 5
|
|
|
|
receive: (event, data) ->
|
|
return if event.substr(0, 6) == 'pusher'
|
|
data = @normalize(event, data) if data.id
|
|
|
|
@processWhenVisible ->
|
|
# TODO remove job:requeued, once sf-restart-event has been merged
|
|
# TODO this also needs to clear logs on build:created if matrix jobs are already loaded
|
|
if event == 'job:created' || event == 'job:requeued'
|
|
if Travis.store.isInStore(Travis.Job, data.job.id)
|
|
Travis.Job.find(data.job.id).clearLog()
|
|
|
|
Ember.run.next ->
|
|
Travis.store.receive(event, data)
|
|
|
|
processSavedCallbacks: ->
|
|
while callback = @callbacksToProcess.shiftObject()
|
|
callback.call(this)
|
|
|
|
processLater: (callback) ->
|
|
@callbacksToProcess.pushObject(callback)
|
|
|
|
processWhenVisible: (callback) ->
|
|
if Visibility.hidden() && Visibility.isSupported()
|
|
@processLater(callback)
|
|
else
|
|
callback.call(this)
|
|
|
|
normalize: (event, data) ->
|
|
switch event
|
|
when 'build:started', 'build:finished'
|
|
data
|
|
when 'job:created', 'job:started', 'job:requeued', 'job:finished', 'job:log'
|
|
data.queue = data.queue.replace('builds.', '') if data.queue
|
|
{ job: data }
|
|
when 'worker:added', 'worker:updated', 'worker:removed'
|
|
{ worker: data }
|
|
|
|
warn: (type, warning) ->
|
|
console.warn(warning) unless @ignoreWarning(warning)
|
|
|
|
ignoreWarning: (warning) ->
|
|
if message = warning.data?.message
|
|
message.indexOf('Existing subscription') == 0 or message.indexOf('No current subscription') == 0
|