Don’t let page_views slow down page unload
See this? https://cl.ly/68e723db54c8 that’s a beforeuload in page_views.js that is taking 335ms in prod to run _before_ the browser can navigate to any other pages. what we are trying to do there in page_views is log the last page view And we can’t use an xhr to do so because it would get aborted when we go to a new page. Where there is a browser API to solve that exact problem: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon This is a screenshot of after: https://cl.ly/fed0076a15eb that sendBeacon call only takes 0.4ms Oh, and we don’t need to do any of this unless ENV.page_view_update_url Is set, so we can defer loading any of it unless that is present Test plan: * page views should work exactly as before * turn on page views * go to a page and sit there for more than 30 seconds * clear your rails log and your browser log * click to go to a new page * you should see a request in your browser Network log to page_views/xxxx-xxxx-xxx-xx-xxx?page_view_token=xxxx * look at your rails server logs, you should see something like: Processing by PageViewsController#update as */* Parameters: {"interaction_seconds"=>"1005”, … ”id"=>"36bcbd62-3a11-44bc-8dd2-69410050ff74"} Change-Id: Ie67cc01dd1524c75916a26fbeffa67a01f490adc Reviewed-on: https://gerrit.instructure.com/196416 Tested-by: Jenkins Reviewed-by: Clay Diffrient <cdiffrient@instructure.com> QA-Review: Clay Diffrient <cdiffrient@instructure.com> Product-Review: Ryan Shaw <ryan@instructure.com>
This commit is contained in:
parent
3a9c4a833c
commit
cd0467a1b1
|
@ -31,7 +31,6 @@ import preventDefault from 'compiled/fn/preventDefault'
|
|||
import 'media_comments'
|
||||
import 'reminders'
|
||||
import 'instructure'
|
||||
import 'page_views'
|
||||
import 'compiled/behaviors/authenticity_token'
|
||||
import 'compiled/behaviors/ujsLinks'
|
||||
import 'compiled/behaviors/admin-links'
|
||||
|
@ -42,6 +41,8 @@ import 'compiled/behaviors/ping'
|
|||
import 'compiled/behaviors/broken-images'
|
||||
import 'LtiThumbnailLauncher'
|
||||
|
||||
if (ENV.page_view_update_url) import('page_views')
|
||||
|
||||
// preventDefault so we dont change the hash
|
||||
// this will make nested apps that use the hash happy
|
||||
$('#skip_navigation_link').on(
|
||||
|
|
|
@ -18,91 +18,82 @@
|
|||
|
||||
import INST from './INST'
|
||||
import $ from 'jquery'
|
||||
import authenticity_token from 'compiled/behaviors/authenticity_token'
|
||||
import './jquery.ajaxJSON'
|
||||
|
||||
$(document).ready(function() {
|
||||
let interactionSeconds = 0,
|
||||
update_url = window.ENV.page_view_update_url
|
||||
eventInTime = false
|
||||
let update_url = window.ENV.page_view_update_url
|
||||
if (update_url) {
|
||||
$(() => {
|
||||
let interactionSeconds = 0
|
||||
|
||||
INST.interaction_contexts = {}
|
||||
INST.interaction_contexts = {}
|
||||
|
||||
if (document.cookie && document.cookie.match(/last_page_view/)) {
|
||||
const match = document.cookie.match(/last_page_view=([^;]+)/)
|
||||
if (match && match[1]) {
|
||||
try {
|
||||
const data = $.parseJSON(unescape(match[1]))
|
||||
if (data && data.url && data.seconds) {
|
||||
setTimeout(function() {
|
||||
$.ajaxJSON(
|
||||
data.url,
|
||||
'PUT',
|
||||
{interaction_seconds: data.seconds},
|
||||
function() {},
|
||||
function() {},
|
||||
3000
|
||||
)
|
||||
if (update_url) {
|
||||
let secondsSinceLastEvent = 0
|
||||
const intervalInSeconds = 60 * 5
|
||||
|
||||
$(document).bind('page_view_update_url_received', function(event, new_update_url) {
|
||||
update_url = new_update_url
|
||||
})
|
||||
|
||||
let updateTrigger
|
||||
$(document).bind('page_view_update', function(event, force) {
|
||||
const data = {}
|
||||
|
||||
if (force || (interactionSeconds > 10 && secondsSinceLastEvent < intervalInSeconds)) {
|
||||
data.interaction_seconds = interactionSeconds
|
||||
$.ajaxJSON(update_url, 'PUT', data, null, function(result, xhr) {
|
||||
if (xhr.status === 422) {
|
||||
clearInterval(updateTrigger)
|
||||
}
|
||||
})
|
||||
interactionSeconds = 0
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
document.cookie = 'last_page_view=; Path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT'
|
||||
}
|
||||
})
|
||||
|
||||
if (update_url) {
|
||||
let secondsSinceLastEvent = 0
|
||||
const intervalInSeconds = 60 * 5
|
||||
updateTrigger = setInterval(function() {
|
||||
$(document).triggerHandler('page_view_update')
|
||||
}, 1000 * intervalInSeconds)
|
||||
|
||||
$(document).bind('page_view_update_url_received', function(event, new_update_url) {
|
||||
update_url = new_update_url
|
||||
})
|
||||
|
||||
let updateTrigger
|
||||
$(document).bind('page_view_update', function(event, force) {
|
||||
const data = {}
|
||||
|
||||
if (force || (interactionSeconds > 10 && secondsSinceLastEvent < intervalInSeconds)) {
|
||||
data.interaction_seconds = interactionSeconds
|
||||
$.ajaxJSON(update_url, 'PUT', data, null, function(result, xhr) {
|
||||
if (xhr.status === 422) {
|
||||
clearInterval(updateTrigger)
|
||||
window.addEventListener(
|
||||
'unload',
|
||||
() => {
|
||||
if (interactionSeconds > 30) {
|
||||
// Use sendBeacon so the request doesn't get cancelled as we navigate away like a normal XHR would,
|
||||
// but because sendBeacon only sends POST requests, we have to use FormData to fake the "_method" to PUT
|
||||
// like Rail's `form_for` does
|
||||
const formData = new FormData()
|
||||
formData.append('interaction_seconds', interactionSeconds)
|
||||
formData.append('_method', 'put')
|
||||
formData.append('authenticity_token', authenticity_token())
|
||||
formData.append('utf8', '✓')
|
||||
navigator.sendBeacon(update_url, formData)
|
||||
}
|
||||
})
|
||||
interactionSeconds = 0
|
||||
}
|
||||
})
|
||||
},
|
||||
false
|
||||
)
|
||||
|
||||
updateTrigger = setInterval(function() {
|
||||
$(document).triggerHandler('page_view_update')
|
||||
}, 1000 * intervalInSeconds)
|
||||
|
||||
window.addEventListener('beforeunload', function(e) {
|
||||
if (interactionSeconds > 30) {
|
||||
const value = JSON.stringify({url: update_url, seconds: interactionSeconds})
|
||||
document.cookie = `last_page_view=${escape(value)}; Path=/;`
|
||||
}
|
||||
})
|
||||
|
||||
var eventInTime = false
|
||||
$(document).bind('mousemove keypress mousedown focus', function() {
|
||||
eventInTime = true
|
||||
})
|
||||
setInterval(function() {
|
||||
if (eventInTime) {
|
||||
interactionSeconds++
|
||||
if (INST && INST.interaction_context && INST.interaction_contexts) {
|
||||
INST.interaction_contexts[INST.interaction_context] =
|
||||
(INST.interaction_contexts[INST.interaction_context] || 0) + 1
|
||||
}
|
||||
eventInTime = false
|
||||
if (secondsSinceLastEvent >= intervalInSeconds) {
|
||||
let eventInTime = false
|
||||
$(document).bind('mousemove keypress mousedown focus', function() {
|
||||
eventInTime = true
|
||||
})
|
||||
setInterval(function() {
|
||||
if (eventInTime) {
|
||||
interactionSeconds++
|
||||
if (INST && INST.interaction_context && INST.interaction_contexts) {
|
||||
INST.interaction_contexts[INST.interaction_context] =
|
||||
(INST.interaction_contexts[INST.interaction_context] || 0) + 1
|
||||
}
|
||||
eventInTime = false
|
||||
if (secondsSinceLastEvent >= intervalInSeconds) {
|
||||
secondsSinceLastEvent = 0
|
||||
$(document).triggerHandler('page_view_update')
|
||||
}
|
||||
secondsSinceLastEvent = 0
|
||||
$(document).triggerHandler('page_view_update')
|
||||
} else {
|
||||
secondsSinceLastEvent++
|
||||
}
|
||||
secondsSinceLastEvent = 0
|
||||
} else {
|
||||
secondsSinceLastEvent++
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue