[web] Remove clsi-cache-prompt/survey, split-tests and events (#29510)

* [web] Remove clsi-cache-prompt/survey and split-tests

* [web] Remove initial 50/50 clsi-cache split-test

* [web] Remove synctex-downloaded-from-cache event

* [web] Remove fallback-to-clsi-cache event

* [saas-e2e] fix tests with clsi-cache enabled

GitOrigin-RevId: b5cf2ab073dc866fe398b81fd5afe46422134c80
This commit is contained in:
Jakob Ackermann
2025-11-04 16:20:50 +01:00
committed by Copybot
parent 6aa9e5c855
commit 3586b37491
26 changed files with 45 additions and 596 deletions

View File

@@ -8,6 +8,7 @@ const logger = require('@overleaf/logger')
const Settings = require('@overleaf/settings')
const OError = require('@overleaf/o-error')
const { NotFoundError, InvalidNameError } = require('../Errors/Errors')
const Features = require('../../infrastructure/Features')
const TIMEOUT = 4_000
@@ -64,6 +65,8 @@ function getEgressLabel(fsPath) {
* @return {Promise<void>}
*/
async function clearCache(projectId, userId) {
if (!Features.hasFeature('saas')) return
let path = `/project/${projectId}`
if (userId) {
path += `/user/${userId}`

View File

@@ -3,11 +3,11 @@ import { NotFoundError, ResourceGoneError } from '../Errors/Errors.js'
import ClsiCacheHandler from './ClsiCacheHandler.js'
import DocumentUpdaterHandler from '../DocumentUpdater/DocumentUpdaterHandler.mjs'
import ProjectGetter from '../Project/ProjectGetter.mjs'
import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
import UserGetter from '../User/UserGetter.js'
import Settings from '@overleaf/settings'
import { fetchJson, RequestFailedError } from '@overleaf/fetch-utils'
import Metrics from '@overleaf/metrics'
import Features from '../../infrastructure/Features.js'
/**
* Get the most recent build and metadata
@@ -179,19 +179,7 @@ async function prepareClsiCache(
userId,
{ sourceProjectId, templateId, templateVersionId }
) {
const { variant } = await SplitTestHandler.promises.getAssignmentForUser(
userId,
'populate-clsi-cache'
)
if (variant !== 'enabled') {
// Pre-populate the cache for the users in the split-test for prompts.
const { variant } = await SplitTestHandler.promises.getAssignmentForUser(
userId,
'populate-clsi-cache-for-prompt'
)
if (variant !== 'enabled') return
}
if (!Features.hasFeature('saas')) return
const features = await UserGetter.promises.getUserFeatures(userId)
if (features.compileGroup !== 'priority') return

View File

@@ -21,6 +21,7 @@ import {
fetchStreamWithResponse,
RequestFailedError,
} from '@overleaf/fetch-utils'
import Features from '../../infrastructure/Features.js'
const { z, zz, validateReq } = Validation
const ClsiCookieManager = ClsiCookieManagerFactory(
@@ -64,45 +65,11 @@ async function _getSplitTestOptions(req, res) {
} catch (e) {}
const editorReq = { ...req, query }
// Lookup the clsi-cache flag in the backend.
// We may need to turn off the feature on a short notice, without requiring
// all users to reload their editor page to disable the feature.
const { variant: populateClsiCacheVariant } =
await SplitTestHandler.promises.getAssignment(
editorReq,
res,
'populate-clsi-cache'
)
let populateClsiCache = populateClsiCacheVariant === 'enabled'
let compileFromClsiCache = populateClsiCache // use same split-test
let clsiCachePromptVariant = 'default'
if (!populateClsiCache) {
// Pre-populate the cache for the users in the split-test for prompts.
// Keep the compile from cache disabled for now.
const { variant } = await SplitTestHandler.promises.getAssignment(
editorReq,
res,
'populate-clsi-cache-for-prompt'
)
;({ variant: clsiCachePromptVariant } =
await SplitTestHandler.promises.getAssignment(
editorReq,
res,
'clsi-cache-prompt'
))
populateClsiCache = variant === 'enabled'
compileFromClsiCache = populateClsiCache
}
const pdfDownloadDomain = Settings.pdfDownloadDomain
if (!req.query.enable_pdf_caching) {
// The frontend does not want to do pdf caching.
return {
compileFromClsiCache,
populateClsiCache,
clsiCachePromptVariant,
pdfDownloadDomain,
enablePdfCaching: false,
}
@@ -120,18 +87,12 @@ async function _getSplitTestOptions(req, res) {
if (!enablePdfCaching) {
// Skip the lookup of the chunk size when caching is not enabled.
return {
compileFromClsiCache,
populateClsiCache,
clsiCachePromptVariant,
pdfDownloadDomain,
enablePdfCaching: false,
}
}
const pdfCachingMinChunkSize = await getPdfCachingMinChunkSize(editorReq, res)
return {
compileFromClsiCache,
populateClsiCache,
clsiCachePromptVariant,
pdfDownloadDomain,
enablePdfCaching,
pdfCachingMinChunkSize,
@@ -145,11 +106,10 @@ async function _syncTeX(req, res, direction, validatedOptions) {
if (!buildId?.match(/^[a-f0-9-]+$/)) throw new Error('invalid ?buildId')
const userId = CompileController._getUserIdForCompile(req)
const { compileFromClsiCache } = await _getSplitTestOptions(req, res)
try {
const body = await CompileManager.promises.syncTeX(projectId, userId, {
direction,
compileFromClsiCache,
compileFromClsiCache: Features.hasFeature('saas'),
validatedOptions: {
...validatedOptions,
editorId,
@@ -220,16 +180,12 @@ const _CompileController = {
options.incrementalCompilesEnabled = true
}
let {
compileFromClsiCache,
populateClsiCache,
clsiCachePromptVariant,
enablePdfCaching,
pdfCachingMinChunkSize,
pdfDownloadDomain,
} = await _getSplitTestOptions(req, res)
options.compileFromClsiCache = compileFromClsiCache
options.populateClsiCache = populateClsiCache
let { enablePdfCaching, pdfCachingMinChunkSize, pdfDownloadDomain } =
await _getSplitTestOptions(req, res)
if (Features.hasFeature('saas')) {
options.compileFromClsiCache = true
options.populateClsiCache = true
}
options.enablePdfCaching = enablePdfCaching
if (enablePdfCaching) {
options.pdfCachingMinChunkSize = pdfCachingMinChunkSize
@@ -301,11 +257,6 @@ const _CompileController = {
compileGroup: limits?.compileGroup,
clsiServerId,
clsiCacheShard,
clsiCachePromptVariant: ['alpha', 'priority'].includes(
limits?.compileGroup
)
? clsiCachePromptVariant
: 'default',
validationProblems,
stats,
timings,

View File

@@ -385,8 +385,6 @@ const _ProjectController = {
'visual-preview',
'external-socket-heartbeat',
'null-test-share-modal',
'populate-clsi-cache',
'populate-clsi-cache-for-prompt',
'pdf-caching-cached-url-lookup',
'pdf-caching-mode',
'pdf-caching-prefetch-large',
@@ -820,26 +818,15 @@ const _ProjectController = {
const planDetails = Settings.plans.find(p => p.planCode === planCode)
const projectOwnerHasPremiumOnPageLoad =
ownerFeatures?.compileGroup === 'priority'
if (
projectOwnerHasPremiumOnPageLoad &&
splitTestAssignments['populate-clsi-cache']?.variant !== 'enabled'
) {
await SplitTestHandler.promises.getAssignment(
req,
res,
'clsi-cache-prompt'
)
}
res.render(template, {
title: project.name,
priority_title: true,
bodyClasses: ['editor'],
project_id: project._id,
projectName: project.name,
projectOwnerHasPremiumOnPageLoad,
canUseClsiCache:
Features.hasFeature('saas') &&
ownerFeatures?.compileGroup === 'priority',
user: {
id: userId,
email: user.email,

View File

@@ -1,6 +1,6 @@
meta(name="ol-project_id" content=project_id)
meta(name="ol-projectName" content=projectName)
meta(name="ol-projectOwnerHasPremiumOnPageLoad" data-type="boolean" content=projectOwnerHasPremiumOnPageLoad)
meta(name="ol-canUseClsiCache" data-type="boolean" content=canUseClsiCache)
meta(name="ol-userSettings" data-type="json" content=userSettings)
meta(name="ol-user" data-type="json" content=user)
meta(name="ol-labsExperiments" data-type="json" content=labsExperiments)

View File

@@ -276,19 +276,6 @@
"clicking_delete_will_remove_sso_config_and_clear_saml_data": "",
"clone_with_git": "",
"close": "",
"clsi_cache_prompt_compile_faster": "",
"clsi_cache_prompt_compile_question": "",
"clsi_cache_prompt_compile_same": "",
"clsi_cache_prompt_compile_slower": "",
"clsi_cache_prompt_preview_less": "",
"clsi_cache_prompt_preview_more": "",
"clsi_cache_prompt_preview_question": "",
"clsi_cache_prompt_preview_same": "",
"clsi_cache_prompt_synctex_less": "",
"clsi_cache_prompt_synctex_more": "",
"clsi_cache_prompt_synctex_question": "",
"clsi_cache_prompt_synctex_same": "",
"clsi_cache_prompt_thanks": "",
"clsi_maintenance": "",
"clsi_unavailable": "",
"code_check_failed": "",

View File

@@ -17,7 +17,6 @@ import getMeta from '@/utils/meta'
import PdfClearCacheButton from '@/features/pdf-preview/components/pdf-clear-cache-button'
import PdfDownloadFilesButton from '@/features/pdf-preview/components/pdf-download-files-button'
import RollingBuildSelectedReminder from './rolling-build-selected-reminder'
import ClsiCachePrompt from '@/features/pdf-preview/components/clsi-cache-prompt'
const logsComponents: Array<{
import: { default: ElementType }
@@ -85,7 +84,6 @@ function ErrorLogs({
))}
<TabContent className="error-logs new-error-logs">
<div className="logs-pane-content">
<ClsiCachePrompt />
<RollingBuildSelectedReminder />
{stoppedOnFirstError && includeErrors && <StopOnFirstErrorPrompt />}

View File

@@ -1,146 +0,0 @@
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { sendMB } from '../../../infrastructure/event-tracking'
import usePersistedState from '../../../shared/hooks/use-persisted-state'
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
import { useProjectContext } from '../../../shared/context/project-context'
import OLNotification from '@/shared/components/ol/ol-notification'
import OLButton from '@/shared/components/ol/ol-button'
import '../../../../stylesheets/pages/editor/clsi-cache-prompt.scss'
import { fallBackToClsiCache } from '@/features/pdf-preview/util/pdf-caching-flags'
import { useNewEditorVariant } from '@/features/ide-redesign/utils/new-editor-utils'
const SAY_THANKS_TIMEOUT = 10 * 1000
function ClsiCachePromptContent() {
const { clsiCachePromptVariant, clsiCachePromptSegmentation } =
useCompileContext()
const { projectId } = useProjectContext()
const newEditorVariant = useNewEditorVariant()
const [hasRatedProject, setHasRatedProject] = usePersistedState(
`clsi-cache-prompt:${clsiCachePromptVariant}:${projectId}`,
false,
{ listen: true }
)
const [dismiss, setDismiss] = usePersistedState(
`clsi-cache-prompt:dismiss`,
false,
{ listen: true }
)
const [sayThanks, setSayThanks] = useState(false)
function sendEvent(feedback: string) {
sendMB('clsi-cache-prompt', {
projectId,
clsiCacheEnabled: fallBackToClsiCache,
clsiCachePromptVariant,
newEditorVariant,
...clsiCachePromptSegmentation[clsiCachePromptVariant],
feedback,
})
}
function submitFeedback(feedback: string) {
sendEvent(feedback)
setHasRatedProject(true)
setSayThanks(true)
window.setTimeout(() => {
setSayThanks(false)
}, SAY_THANKS_TIMEOUT)
}
function dismissFeedback() {
sendEvent('dismiss')
setDismiss(true)
}
const { t } = useTranslation()
if (clsiCachePromptVariant === 'default') return null
switch (true) {
case sayThanks:
return (
<OLNotification
type="info"
className="clsi-cache-prompt"
onDismiss={() => setSayThanks(false)}
content={t('clsi_cache_prompt_thanks')}
/>
)
case dismiss || hasRatedProject:
return null
case clsiCachePromptSegmentation?.[clsiCachePromptVariant] != null: {
let question: string
let answers: Record<string, string>
switch (clsiCachePromptVariant) {
case 'compile': {
question = t('clsi_cache_prompt_compile_question')
answers = {
slower: t('clsi_cache_prompt_compile_slower'),
same: t('clsi_cache_prompt_compile_same'),
faster: t('clsi_cache_prompt_compile_faster'),
}
break
}
case 'preview':
case 'preview-error': {
question = t('clsi_cache_prompt_preview_question')
answers = {
less: t('clsi_cache_prompt_preview_less'),
same: t('clsi_cache_prompt_preview_same'),
more: t('clsi_cache_prompt_preview_more'),
}
break
}
case 'synctex': {
question = t('clsi_cache_prompt_synctex_question')
answers = {
less: t('clsi_cache_prompt_synctex_less'),
same: t('clsi_cache_prompt_synctex_same'),
more: t('clsi_cache_prompt_synctex_more'),
}
break
}
}
return (
<OLNotification
type="info"
className="clsi-cache-prompt"
customIcon={<></>}
isDismissible
onDismiss={dismissFeedback}
content={
<>
{question}
<br />
{Object.entries(answers).map(([feedback, text]) => (
<OLButton
variant="secondary"
size="sm"
onClick={() => submitFeedback(feedback)}
key={feedback}
>
{text}
</OLButton>
))}
</>
}
/>
)
}
default:
return null
}
}
function ClsiCachePrompt() {
const { clsiCachePromptVariant, showLogs } = useCompileContext()
const onlyInLogsPane = clsiCachePromptVariant === 'preview-error'
if (clsiCachePromptVariant === 'default' || showLogs !== onlyInLogsPane) {
return null
}
return <ClsiCachePromptContent />
}
export default memo(ClsiCachePrompt)

View File

@@ -25,14 +25,8 @@ type PdfJsViewerProps = {
function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
const { projectId } = useProjectContext()
const {
setError,
firstRenderDone,
highlights,
position,
setPosition,
setClsiCachePromptSegmentation,
} = useCompileContext()
const { setError, firstRenderDone, highlights, position, setPosition } =
useCompileContext()
const { setLoadingError } = usePdfPreviewContext()
@@ -88,10 +82,6 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
}, [])
const [startFetch, setStartFetch] = useState(0)
const startFetchRef = useRef(startFetch)
useEffect(() => {
startFetchRef.current = startFetch
}, [startFetch])
// listen for events and trigger rendering.
// Do everything in one effect to mitigate de-sync between events.
@@ -158,27 +148,6 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
setPage(event.pageNumber)
}
const handleRenderedClsiCachePrompt = () => {
const delay = Math.ceil(
(performance.now() - startFetchRef.current) / 1_000
)
if (delay > 30) {
setClsiCachePromptSegmentation(prev => {
// Always overwrite segmentation; emit the greatest delay.
return {
...prev,
preview: {
previewFailed: false,
delay,
usedClsiCache: pdfJsWrapper.usedClsiCache(),
},
}
})
pdfJsWrapper.eventBus.off('pagerendered', handleRenderedClsiCachePrompt)
}
}
pdfJsWrapper.eventBus.on('pagerendered', handleRenderedClsiCachePrompt)
// `pagesinit` fires when the data for rendering the first page is ready.
pdfJsWrapper.eventBus.on('pagesinit', handlePagesinit)
// `pagerendered` fires when a page was actually rendered.
@@ -195,22 +164,15 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
pdfJsWrapper.eventBus.off('pagerendered', handleRenderedInitialPageNumber)
pdfJsWrapper.eventBus.off('scalechanging', handleScaleChanged)
pdfJsWrapper.eventBus.off('pagechanging', handlePageChanging)
pdfJsWrapper.eventBus.off('pagerendered', handleRenderedClsiCachePrompt)
}
}, [
pdfJsWrapper,
firstRenderDone,
startFetch,
setClsiCachePromptSegmentation,
])
}, [pdfJsWrapper, firstRenderDone, startFetch])
// load the PDF document from the URL
useEffect(() => {
if (pdfJsWrapper && url) {
setInitialised(false)
setError(undefined)
const t0 = performance.now()
setStartFetch(t0)
setStartFetch(performance.now())
const abortController = new AbortController()
const handleFetchError = (err: any) => {
@@ -218,21 +180,6 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
// The error is already logged at the call-site with additional context.
if (err instanceof PDFJS.ResponseException && err.missing) {
setError('rendering-error-expected')
setClsiCachePromptSegmentation(prev => {
if (prev['preview-error'] !== null) {
return prev // keep delay segmentation from first error.
}
const delay = Math.ceil((performance.now() - t0) / 1_000)
return {
...prev,
preview: null,
'preview-error': {
previewFailed: true,
delay,
usedClsiCache: pdfJsWrapper.usedClsiCache(),
},
}
})
} else {
setError('rendering-error')
}
@@ -243,9 +190,6 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
if (doc) {
setTotalPages(doc.numPages)
}
setClsiCachePromptSegmentation(prev => {
return { ...prev, preview: null, 'preview-error': null }
})
})
.catch(error => {
if (abortController.signal.aborted) return
@@ -256,14 +200,7 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
abortController.abort()
}
}
}, [
pdfJsWrapper,
url,
pdfFile,
setError,
setStartFetch,
setClsiCachePromptSegmentation,
])
}, [pdfJsWrapper, url, pdfFile, setError, setStartFetch])
// listen for scroll events
useEffect(() => {

View File

@@ -16,7 +16,6 @@ import { useDetachCompileContext as useCompileContext } from '../../../shared/co
import PdfLogEntry from './pdf-log-entry'
import { usePdfPreviewContext } from '@/features/pdf-preview/components/pdf-preview-provider'
import getMeta from '@/utils/meta'
import ClsiCachePrompt from '@/features/pdf-preview/components/clsi-cache-prompt'
function PdfLogsViewer({ alwaysVisible = false }: { alwaysVisible?: boolean }) {
const {
@@ -27,7 +26,6 @@ function PdfLogsViewer({ alwaysVisible = false }: { alwaysVisible?: boolean }) {
validationIssues,
showLogs,
stoppedOnFirstError,
clsiCachePromptVariant,
} = useCompileContext()
const { loadingError } = usePdfPreviewContext()
@@ -44,8 +42,6 @@ function PdfLogsViewer({ alwaysVisible = false }: { alwaysVisible?: boolean }) {
data-testid="logs-pane"
>
<div className="logs-pane-content">
{clsiCachePromptVariant === 'preview-error' && <ClsiCachePrompt />}
<RollingBuildSelectedReminder />
{codeCheckFailed && <PdfCodeCheckFailedNotice />}

View File

@@ -14,7 +14,6 @@ import importOverleafModules from '../../../../macros/import-overleaf-module.mac
import PdfCodeCheckFailedBanner from '@/features/ide-redesign/components/pdf-preview/pdf-code-check-failed-banner'
import getMeta from '@/utils/meta'
import NewPdfLogsViewer from '@/features/ide-redesign/components/pdf-preview/pdf-logs-viewer'
import ClsiCachePrompt from './clsi-cache-prompt'
function PdfPreviewPane() {
const { pdfUrl } = useCompileContext()
@@ -44,7 +43,6 @@ function PdfPreviewPane() {
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
<div className="pdf-viewer" data-testid="pdf-viewer">
<PdfViewer />
<ClsiCachePrompt />
</div>
</Suspense>
{newEditor ? <NewPdfLogsViewer /> : <PdfLogsViewer />}

View File

@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState, useRef } from 'react'
import { useProjectContext } from '../../../shared/context/project-context'
import { FetchError, getJSON } from '../../../infrastructure/fetch-json'
import { getJSON } from '../../../infrastructure/fetch-json'
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
import useIsMounted from '../../../shared/hooks/use-is-mounted'
import useAbortController from '../../../shared/hooks/use-abort-controller'
@@ -21,7 +21,6 @@ import {
showFileErrorToast,
showSynctexRequestErrorToast,
} from '@/features/pdf-preview/components/synctex-toasts'
import { sendMB } from '@/infrastructure/event-tracking'
export default function useSynctex(): {
syncToPdf: () => void
@@ -33,14 +32,8 @@ export default function useSynctex(): {
const { projectId, project } = useProjectContext()
const rootDocId = project?.rootDocId
const {
clsiServerId,
pdfFile,
position,
setShowLogs,
setHighlights,
setClsiCachePromptSegmentation,
} = useCompileContext()
const { clsiServerId, pdfFile, position, setShowLogs, setHighlights } =
useCompileContext()
const { selectedEntities } = useFileTreeData()
const { findEntityByPath, dirname, pathInFolder } = useFileTreePathContext()
@@ -127,35 +120,8 @@ export default function useSynctex(): {
.then(data => {
setShowLogs(false)
setHighlights(data.pdf)
setClsiCachePromptSegmentation(prev => {
return {
...prev,
synctex: {
direction: 'pdf',
navigationFailed: false,
restoredFromCache: data.downloadedFromCache,
},
}
})
if (data.downloadedFromCache) {
sendMB('synctex-downloaded-from-cache', {
projectId,
method: 'code',
})
}
})
.catch(error => {
if (error instanceof FetchError && error.response?.status === 404) {
setClsiCachePromptSegmentation(prev => {
return {
...prev,
synctex: {
direction: 'pdf',
navigationFailed: true,
},
}
})
}
showSynctexRequestErrorToast()
debugConsole.error(error)
})
@@ -173,7 +139,6 @@ export default function useSynctex(): {
setShowLogs,
setHighlights,
setSyncToPdfInFlight,
setClsiCachePromptSegmentation,
signal,
]
)
@@ -263,35 +228,8 @@ export default function useSynctex(): {
.then(data => {
const [{ file, line }] = data.code
goToCodeLine(file, line, selectText)
setClsiCachePromptSegmentation(prev => {
return {
...prev,
synctex: {
direction: 'code',
navigationFailed: false,
restoredFromCache: data.downloadedFromCache,
},
}
})
if (data.downloadedFromCache) {
sendMB('synctex-downloaded-from-cache', {
projectId,
method: 'pdf',
})
}
})
.catch(error => {
if (error instanceof FetchError && error.response?.status === 404) {
setClsiCachePromptSegmentation(prev => {
return {
...prev,
synctex: {
direction: 'code',
navigationFailed: true,
},
}
})
}
debugConsole.error(error)
showSynctexRequestErrorToast()
})
@@ -308,7 +246,6 @@ export default function useSynctex(): {
signal,
isMounted,
setSyncToCodeInFlight,
setClsiCachePromptSegmentation,
goToCodeLine,
]
)

View File

@@ -153,7 +153,6 @@ export default class DocumentCompiler {
)
const compileTimeClientE2E = Math.ceil(performance.now() - t0)
if (data.timings) data.timings.compileTimeClientE2E = compileTimeClientE2E
const { deliveryLatencies, firstRenderDone } = trackPdfDownload(
data,
compileTimeClientE2E,

View File

@@ -29,10 +29,4 @@ export const prefetchingEnabled = isFlagEnabled('pdf-caching-prefetching')
export const prefetchLargeEnabled = isFlagEnabled('pdf-caching-prefetch-large')
export const enablePdfCaching = isFlagEnabled('pdf-caching-mode')
export const trackPdfDownloadEnabled = isFlagEnabled('track-pdf-download')
export const projectOwnerHasPremiumOnPageLoad = getMeta(
'ol-projectOwnerHasPremiumOnPageLoad'
)
export const fallBackToClsiCache =
projectOwnerHasPremiumOnPageLoad &&
(isFlagEnabled('populate-clsi-cache') ||
isFlagEnabled('populate-clsi-cache-for-prompt'))
export const canUseClsiCache = getMeta('ol-canUseClsiCache')

View File

@@ -8,13 +8,11 @@ import {
prefetchingEnabled,
prefetchLargeEnabled,
trackPdfDownloadEnabled,
fallBackToClsiCache,
canUseClsiCache,
} from './pdf-caching-flags'
import { isNetworkError } from '@/utils/is-network-error'
import { debugConsole } from '@/utils/debugging'
import { PDFJS } from './pdf-js'
import { sendMB } from '@/infrastructure/event-tracking'
import getMeta from '@/utils/meta'
import { PDFFile, PDFRange, ProcessedPDFFile } from '@ol-types/compile'
import { PdfCachingMetricsFull } from './types'
@@ -26,7 +24,6 @@ export function generatePdfCachingTransportFactory() {
if (!enablePdfCaching && !trackPdfDownloadEnabled) {
return () => undefined
}
const projectId = getMeta('ol-project_id')
const usageScore = new Map()
const cachedUrls = new Map()
const metrics: PdfCachingMetricsFull = Object.assign(getPdfCachingMetrics(), {
@@ -59,7 +56,6 @@ export function generatePdfCachingTransportFactory() {
leanPdfRanges: PDFRange[]
handleFetchError: (error: any) => void
startTime: number
sentEventFallbackToClsiCache: boolean
queryForChunks: string
constructor({
@@ -89,7 +85,6 @@ export function generatePdfCachingTransportFactory() {
this.handleFetchError = handleFetchError
this.abortController = abortController
this.startTime = performance.now()
this.sentEventFallbackToClsiCache = false
const params = new URL(url).searchParams
// drop no needed params
@@ -103,7 +98,6 @@ export function generatePdfCachingTransportFactory() {
}
requestDataRange(start: number, end: number) {
let recordFallbackToClsiCache = false
const abortSignal = this.abortController.signal
const getDebugInfo = () => ({
// Sentry does not serialize objects in twice nested objects.
@@ -152,7 +146,7 @@ export function generatePdfCachingTransportFactory() {
)
}
const canTryFromCache = (err: any) => {
if (!fallBackToClsiCache) return false
if (!canUseClsiCache) return false
if (!is404(err)) return false
return !usesCache((OError.getFullInfo(err) as { url: string }).url)
}
@@ -183,7 +177,6 @@ export function generatePdfCachingTransportFactory() {
this.pdfFile.ranges = this.pdfFile.ranges.filter(r =>
cachedUrls.has(r.hash)
)
recordFallbackToClsiCache = true
return blob
})
.catch(err => {
@@ -215,9 +208,6 @@ export function generatePdfCachingTransportFactory() {
abortSignal,
canTryFromCache,
fallbackToCacheURL: getOutputPDFURLFromCache(),
recordFallbackToClsiCache: () => {
recordFallbackToClsiCache = true
},
})
.catch(err => {
if (abortSignal.aborted) return
@@ -278,14 +268,6 @@ export function generatePdfCachingTransportFactory() {
})
.then(blob => {
if (abortSignal.aborted) return
if (recordFallbackToClsiCache && !this.sentEventFallbackToClsiCache) {
// Record once per PDF preview. Technically we should record once per 90min (output cache age), but keep it simple for now.
this.sentEventFallbackToClsiCache = true
sendMB('fallback-to-clsi-cache', {
projectId,
ageMS: Math.ceil(performance.now() - this.startTime),
})
}
this.onDataRange(start, blob ? new Uint8Array(blob) : null)
})
.catch(err => {

View File

@@ -598,7 +598,6 @@ async function fetchChunk({
canTryFromCache,
fallbackToCacheURL,
file,
recordFallbackToClsiCache,
}: {
chunk: Chunk | PDFRange<Uint8Array> | Chunk[]
url: string
@@ -609,7 +608,6 @@ async function fetchChunk({
canTryFromCache: (error: any) => boolean
fallbackToCacheURL: string
file: ProcessedPDFFile
recordFallbackToClsiCache: () => void
}) {
const estimatedSize = Array.isArray(chunk)
? estimateSizeOfMultipartResponse(chunk)
@@ -676,7 +674,6 @@ async function fetchChunk({
try {
response = await fetchWithBrowserCacheFallback(url, init)
checkChunkResponse(response, estimatedSize, init)
recordFallbackToClsiCache()
} catch (err2) {
throw err1
}
@@ -836,7 +833,6 @@ export async function fetchRange({
abortSignal,
canTryFromCache,
fallbackToCacheURL,
recordFallbackToClsiCache,
}: {
url: string
start: number
@@ -853,7 +849,6 @@ export async function fetchRange({
abortSignal: AbortSignal
canTryFromCache: (error: any) => boolean
fallbackToCacheURL: string
recordFallbackToClsiCache: () => void
}) {
const timer = new Timer()
timer.startBlockingCompute()
@@ -1011,7 +1006,6 @@ export async function fetchRange({
canTryFromCache,
fallbackToCacheURL,
file,
recordFallbackToClsiCache,
})
timer.startBlockingCompute()
const boundary = getMultipartBoundary(response, chunk)

View File

@@ -48,10 +48,6 @@ export default class PDFJSWrapper {
this.pdfCachingTransportFactory = generatePdfCachingTransportFactory()
}
usedClsiCache() {
return !!this.pdfCachingTransportFactory?.sentEventFallbackToClsiCache
}
// load a document from a URL
async loadDocument({
url,

View File

@@ -42,17 +42,6 @@ export type PdfFileDataList = {
archive?: PdfFileArchiveData
}
export type ClsiCachePromptVariant =
| 'default'
| 'compile'
| 'preview'
| 'preview-error'
| 'synctex'
export type ClsiCachePromptSegmentation = Record<
ClsiCachePromptVariant,
Record<string, any> | null
>
export type HighlightData = {
page: number
h: number

View File

@@ -56,9 +56,6 @@ export const DetachCompileProvider: FC<React.PropsWithChildren> = ({
setStopOnValidationError: _setStopOnValidationError,
showLogs: _showLogs,
showCompileTimeWarning: _showCompileTimeWarning,
clsiCachePromptVariant: _clsiCachePromptVariant,
clsiCachePromptSegmentation: _clsiCachePromptSegmentation,
setClsiCachePromptSegmentation: _setClsiCachePromptSegmentation,
stopOnFirstError: _stopOnFirstError,
stopOnValidationError: _stopOnValidationError,
stoppedOnFirstError: _stoppedOnFirstError,
@@ -204,24 +201,6 @@ export const DetachCompileProvider: FC<React.PropsWithChildren> = ({
'detacher',
'detached'
)
const [clsiCachePromptVariant] = useDetachStateWatcher(
'clsiCachePromptVariant',
_clsiCachePromptVariant,
'detacher',
'detached'
)
const [clsiCachePromptSegmentation] = useDetachStateWatcher(
'clsiCachePromptSegmentation',
_clsiCachePromptSegmentation,
'detacher',
'detached'
)
const setClsiCachePromptSegmentation = useDetachAction(
'setClsiCachePromptSegmentation',
_setClsiCachePromptSegmentation,
'detacher',
'detached'
)
const [stopOnFirstError] = useDetachStateWatcher(
'stopOnFirstError',
_stopOnFirstError,
@@ -440,9 +419,6 @@ export const DetachCompileProvider: FC<React.PropsWithChildren> = ({
setStopOnValidationError,
showLogs,
showCompileTimeWarning,
clsiCachePromptVariant,
clsiCachePromptSegmentation,
setClsiCachePromptSegmentation,
startCompile,
stopCompile,
stopOnFirstError,
@@ -496,9 +472,6 @@ export const DetachCompileProvider: FC<React.PropsWithChildren> = ({
setStopOnValidationError,
showCompileTimeWarning,
showLogs,
clsiCachePromptVariant,
clsiCachePromptSegmentation,
setClsiCachePromptSegmentation,
startCompile,
stopCompile,
stopOnFirstError,

View File

@@ -39,20 +39,17 @@ import { useFeatureFlag } from '@/shared/context/split-test-context'
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
import { getJSON } from '@/infrastructure/fetch-json'
import { CompileResponseData, PDFFile } from '../../../../types/compile'
import { CompileResponseData } from '../../../../types/compile'
import {
PdfScrollPosition,
usePdfScrollPosition,
} from '@/shared/hooks/use-pdf-scroll-position'
import {
ClsiCachePromptSegmentation,
ClsiCachePromptVariant,
DeliveryLatencies,
HighlightData,
LogEntry,
PdfFileDataList,
} from '@/features/pdf-preview/util/types'
import { getSplitTestVariant, isSplitTestEnabled } from '@/utils/splitTestUtils'
import { captureException } from '@/infrastructure/error-reporter'
import OError from '@overleaf/o-error'
import getMeta from '@/utils/meta'
@@ -101,11 +98,6 @@ export type CompileContext = {
setStopOnValidationError: (value: boolean) => void
showCompileTimeWarning: boolean
showLogs: boolean
clsiCachePromptVariant: ClsiCachePromptVariant
clsiCachePromptSegmentation: ClsiCachePromptSegmentation
setClsiCachePromptSegmentation: Dispatch<
SetStateAction<ClsiCachePromptSegmentation>
>
stopOnFirstError: boolean
stopOnValidationError: boolean
stoppedOnFirstError: boolean
@@ -206,9 +198,7 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
const [compiledOnce, setCompiledOnce] = useState(false)
// fetch initial compile response from cache
const [initialCompileFromCache, setInitialCompileFromCache] = useState(
getMeta('ol-projectOwnerHasPremiumOnPageLoad') &&
(isSplitTestEnabled('populate-clsi-cache') ||
isSplitTestEnabled('populate-clsi-cache-for-prompt')) &&
getMeta('ol-canUseClsiCache') &&
// Avoid fetching the initial compile from cache in PDF detach tab
role !== 'detached'
)
@@ -217,7 +207,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
useState(false)
// Raw data from clsi-cache, will need post-processing and check settings
const [dataFromCache, setDataFromCache] = useState<CompileResponseData>()
const [compileFromCacheStartedAt, setCompileFromCacheStartedAt] = useState(0)
// whether the cache is being cleared
const [clearingCache, setClearingCache] = useState(false)
@@ -225,18 +214,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
// whether the logs should be visible
const [showLogs, setShowLogs] = useState(false)
// flags for clsi-cache prompt
const [clsiCachePromptVariant, setClsiCachePromptVariant] =
useState<ClsiCachePromptVariant>('default')
const [clsiCachePromptSegmentation, setClsiCachePromptSegmentation] =
useState<ClsiCachePromptSegmentation>({
default: null,
compile: null,
preview: null,
'preview-error': null,
synctex: null,
})
// whether the compile dropdown arrow should be animated
const [animateCompileDropdownArrow, setAnimateCompileDropdownArrow] =
useState(false)
@@ -377,7 +354,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
useEffect(() => {
if (initialCompileFromCache && !pendingInitialCompileFromCache) {
setPendingInitialCompileFromCache(true)
setCompileFromCacheStartedAt(performance.now())
getJSON(`/project/${projectId}/output/cached/output.overleaf.json`, {
signal: AbortSignal.timeout(5_000),
})
@@ -425,14 +401,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
if (settingsUpToDate) {
sendMB('compile-from-cache', { projectId })
dataFromCache.clsiCachePromptVariant = getSplitTestVariant(
'clsi-cache-prompt',
'default'
) as ClsiCachePromptVariant
if (!dataFromCache.timings) dataFromCache.timings = {}
dataFromCache.timings.compileTimeClientE2E = Math.ceil(
performance.now() - compileFromCacheStartedAt
)
setData(dataFromCache)
setCompiledOnce(true)
}
@@ -441,7 +409,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
setPendingInitialCompileFromCache(false)
}, [
projectId,
compileFromCacheStartedAt,
dataFromCache,
joinedOnce,
currentDocument,
@@ -519,32 +486,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
if (data.clsiServerId) {
setClsiServerId(data.clsiServerId) // set in scope, for PdfSynctexController
}
setClsiCachePromptVariant(data.clsiCachePromptVariant ?? 'default')
setClsiCachePromptSegmentation(prev => {
if (
!(
data.status === 'success' &&
(data.fromCache || data.stats?.isInitialCompile === 1)
)
) {
return { ...prev, compile: null }
}
const pdfSize = (
data.outputFiles.find(f => f.path === 'output.pdf') as PDFFile
)?.size
return {
...prev,
compile: {
pdfSize,
isCompileFromCache: Boolean(data.fromCache),
isInitialCompile: data.stats?.isInitialCompile === 1,
restoredClsiCache: data.stats?.restoredClsiCache === 1,
compileTimeServerE2E: data.timings?.compileE2E,
compileTimeClientE2E: data.timings?.compileTimeClientE2E,
clsiCacheEnabled: Boolean(data.clsiCacheShard),
},
}
})
if (data.outputFiles) {
const outputFiles = new Map()
@@ -840,9 +781,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
setStopOnFirstError,
setStopOnValidationError,
showLogs,
clsiCachePromptVariant,
clsiCachePromptSegmentation,
setClsiCachePromptSegmentation,
startCompile,
stopCompile,
stopOnFirstError,
@@ -893,9 +831,6 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
setStopOnValidationError,
showCompileTimeWarning,
showLogs,
clsiCachePromptVariant,
clsiCachePromptSegmentation,
setClsiCachePromptSegmentation,
startCompile,
stopCompile,
stopOnFirstError,

View File

@@ -82,10 +82,11 @@ export interface Meta {
'ol-anonymous': boolean
'ol-baseAssetPath': string
'ol-brandVariation': Record<string, any>
'ol-canUseAddSeatsFeature': boolean
'ol-canUseClsiCache': boolean
'ol-canUseFlexibleLicensing': boolean
// dynamic keys based on permissions
'ol-canUseAddSeatsFeature': boolean
'ol-canUseFlexibleLicensing': boolean
'ol-cannot-add-secondary-email': boolean
'ol-cannot-change-password': boolean
'ol-cannot-delete-own-account': boolean
@@ -96,6 +97,7 @@ export interface Meta {
'ol-cannot-reactivate-subscription': boolean
'ol-cannot-use-ai': boolean
'ol-capabilities': Array<'dropbox' | 'chat' | 'use-ai' | 'link-sharing'>
'ol-compileSettings': {
compileTimeout: number
}
@@ -224,7 +226,6 @@ export interface Meta {
'ol-project': any // TODO
'ol-projectEntityCounts': { files: number; docs: number }
'ol-projectName': string
'ol-projectOwnerHasPremiumOnPageLoad': boolean
'ol-projectSyncSuccessMessage': string
'ol-projectTags': Tag[]
'ol-project_id': string

View File

@@ -1,27 +0,0 @@
@import '../../foundations/spacing';
.clsi-cache-prompt {
width: auto; // Do not expand to full width.
.pdf-viewer &,
.pdf-error-state & {
position: fixed;
bottom: 0;
right: 0.5rem; // Add space for scrollbar.
margin: 1rem;
}
.notification-icon {
display: none; // Hide the (i) icon.
}
.notification-close-btn {
// Put even spacing between question, X and notification border.
padding: 0 0 0 (16px - 5.5px); // The X button already has 5.5px padding.
}
.btn {
white-space: nowrap;
margin: 10px 0 0 10px;
}
}

View File

@@ -353,19 +353,6 @@
"clicking_delete_will_remove_sso_config_and_clear_saml_data": "Clicking <0>Delete</0> will remove your SSO configuration and unlink all users. You can only do this when SSO is disabled in your group settings.",
"clone_with_git": "Clone with Git",
"close": "Close",
"clsi_cache_prompt_compile_faster": "Faster",
"clsi_cache_prompt_compile_question": "Was this compile different than usual?",
"clsi_cache_prompt_compile_same": "Same",
"clsi_cache_prompt_compile_slower": "Slower",
"clsi_cache_prompt_preview_less": "Less reliable",
"clsi_cache_prompt_preview_more": "More reliable",
"clsi_cache_prompt_preview_question": "How are you finding the PDF preview at the moment?",
"clsi_cache_prompt_preview_same": "Same as usual",
"clsi_cache_prompt_synctex_less": "Less reliable",
"clsi_cache_prompt_synctex_more": "More reliable",
"clsi_cache_prompt_synctex_question": "How are you finding the navigation between text and PDF?",
"clsi_cache_prompt_synctex_same": "Same as usual",
"clsi_cache_prompt_thanks": "Thanks for the feedback!",
"clsi_maintenance": "The compile servers are down for maintenance, and will be back shortly.",
"clsi_unavailable": "Sorry, the compile server for your project was temporarily unavailable. Please try again in a few moments.",
"cn": "Chinese (Simplified)",

View File

@@ -48,10 +48,7 @@ describe('<PdfPreview/>', function () {
'ol-compilesUserContentDomain',
'https://compiles-user.dev-overleaf.com'
)
window.metaAttributesCache.set('ol-splitTestVariants', {
'populate-clsi-cache': 'enabled',
})
window.metaAttributesCache.set('ol-projectOwnerHasPremiumOnPageLoad', true)
window.metaAttributesCache.set('ol-canUseClsiCache', true)
window.metaAttributesCache.set('ol-compileSettings', {
compileTimeout: 240,
})

View File

@@ -223,7 +223,6 @@ describe('CompileController', function () {
url: `/project/${ctx.projectId}/user/wat/build/${ctx.build_id}/output/output.zip`,
type: 'zip',
},
clsiCachePromptVariant: 'default',
pdfDownloadDomain: 'https://compiles.overleaf.test',
})
)
@@ -248,7 +247,6 @@ describe('CompileController', function () {
timings: undefined,
outputUrlPrefix: '/zone/b',
buildId: ctx.build_id,
clsiCachePromptVariant: 'default',
})
await ctx.CompileController.compile(ctx.req, ctx.res, ctx.next)
})
@@ -270,7 +268,6 @@ describe('CompileController', function () {
url: `/project/${ctx.projectId}/user/wat/build/${ctx.build_id}/output/output.zip`,
type: 'zip',
},
clsiCachePromptVariant: 'default',
outputUrlPrefix: '/zone/b',
pdfDownloadDomain: 'https://compiles.overleaf.test/zone/b',
})
@@ -296,8 +293,8 @@ describe('CompileController', function () {
ctx.user_id,
{
isAutoCompile: false,
compileFromClsiCache: false,
populateClsiCache: false,
compileFromClsiCache: true,
populateClsiCache: true,
enablePdfCaching: false,
fileLineErrors: false,
stopOnFirstError: false,
@@ -321,7 +318,6 @@ describe('CompileController', function () {
url: `/project/${ctx.projectId}/user/wat/build/${ctx.build_id}/output/output.zip`,
type: 'zip',
},
clsiCachePromptVariant: 'default',
})
)
})
@@ -339,8 +335,8 @@ describe('CompileController', function () {
ctx.user_id,
{
isAutoCompile: true,
compileFromClsiCache: false,
populateClsiCache: false,
compileFromClsiCache: true,
populateClsiCache: true,
enablePdfCaching: false,
fileLineErrors: false,
stopOnFirstError: false,
@@ -362,8 +358,8 @@ describe('CompileController', function () {
ctx.user_id,
{
isAutoCompile: false,
compileFromClsiCache: false,
populateClsiCache: false,
compileFromClsiCache: true,
populateClsiCache: true,
enablePdfCaching: false,
draft: true,
fileLineErrors: false,
@@ -386,8 +382,8 @@ describe('CompileController', function () {
ctx.user_id,
{
isAutoCompile: false,
compileFromClsiCache: false,
populateClsiCache: false,
compileFromClsiCache: true,
populateClsiCache: true,
enablePdfCaching: false,
fileLineErrors: false,
stopOnFirstError: false,
@@ -671,7 +667,7 @@ describe('CompileController', function () {
ctx.user_id,
{
direction: 'code',
compileFromClsiCache: false,
compileFromClsiCache: true,
validatedOptions: {
file,
line,
@@ -721,7 +717,7 @@ describe('CompileController', function () {
ctx.user_id,
{
direction: 'pdf',
compileFromClsiCache: false,
compileFromClsiCache: true,
validatedOptions: {
page,
h,

View File

@@ -1,5 +1,3 @@
import { ClsiCachePromptVariant } from '@/features/pdf-preview/util/types'
export type Chunk = {
start: number
end: number
@@ -74,7 +72,6 @@ export type CompileResponseData = {
stats?: Record<string, number>
timings?: Record<string, number>
outputFilesArchive?: CompileOutputFile
clsiCachePromptVariant?: ClsiCachePromptVariant
// assigned on response body by DocumentCompiler in frontend
rootDocId?: string | null