mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
[k8s] clsi-cache: migrate to StatefulSet (#30886)
* [k8s] clsi-cache: migrate to StatefulSet * clsi-cache: optimize ILB services for GKE subsetting Update the new clsi-cache internal load balancer services to use optimal settings for GKE subsetting (NEG backends): - set allocateLoadBalancerNodePorts: false (not needed with NEGs) - set externalTrafficPolicy: Local (preserve source IP, keep traffic in zone) - add trafficDistribution: PreferClose (zone affinity) These settings ensure traffic from CLSI VMs stays within the same zone when possible, reducing latency and cross-zone network costs. * [k8s] clsi-cache: add missing resource paths * [clsi] exclude readOnly clsi-cache shards --------- Co-authored-by: Daniel Kontsek <daniel.kontsek@overleaf.com> GitOrigin-RevId: 34f18b319a0e859ff149a135131c95a44bc674d6
This commit is contained in:
committed by
Copybot
co-authored by
Daniel Kontsek
parent
49591a5190
commit
0ee8b25298
@@ -38,30 +38,32 @@ const MIGRATE_UNTIL = new Date('2026-01-21').getTime()
|
||||
|
||||
/**
|
||||
* @param {string} projectId
|
||||
* @return {{shard: string, url: string}}
|
||||
* @return {{shard: string, url: string}|undefined}
|
||||
*/
|
||||
function getShard(projectId) {
|
||||
function getAvailableShard(projectId) {
|
||||
// Layout of mongodb object id bytes:
|
||||
// [timestamp 4bytes][random per machine 5bytes][counter 3bytes]
|
||||
// [32bit 4bytes]
|
||||
const last4Bytes = Buffer.from(projectId, 'hex').subarray(8, 12)
|
||||
const counter = last4Bytes.readUInt32BE()
|
||||
const nShards = Settings.apis.clsiCache.shards.length
|
||||
|
||||
// Use the "counter" part of the id for a stable assignment with an even
|
||||
// distribution. This is where the current data resides.
|
||||
const preferredShard = counter % nShards
|
||||
|
||||
// Gradually migrate over to the crc base shard assignment.
|
||||
const remaining = MIGRATE_UNTIL - Date.now()
|
||||
if ((counter % 100) / 100 < remaining / (MIGRATE_UNTIL - MIGRATE_FROM)) {
|
||||
const shard = Settings.apis.clsiCache.shards[preferredShard]
|
||||
if (!isCircuitBreakerTripped(shard.url)) return shard
|
||||
let shards = Settings.apis.clsiCache.shards.slice(
|
||||
0,
|
||||
Settings.apis.clsiCache.currentShards
|
||||
)
|
||||
const now = Date.now()
|
||||
if (
|
||||
now > MIGRATE_FROM &&
|
||||
now < MIGRATE_UNTIL &&
|
||||
(counter % 100) / 100 <
|
||||
(MIGRATE_UNTIL - now) / (MIGRATE_UNTIL - MIGRATE_FROM)
|
||||
) {
|
||||
shards = Settings.apis.clsiCache.shards.slice(
|
||||
0,
|
||||
Settings.apis.clsiCache.desiredShards
|
||||
)
|
||||
}
|
||||
|
||||
// Then use a crc for generating a stable sequence of shards with even
|
||||
// distribution to try next.
|
||||
const shards = Settings.apis.clsiCache.shards.slice(0)
|
||||
let i = 0
|
||||
while (shards.length > 0) {
|
||||
const idx = crc32(`${projectId}-${i++}`) % shards.length
|
||||
@@ -69,8 +71,7 @@ function getShard(projectId) {
|
||||
if (!isCircuitBreakerTripped(candidate.url)) return candidate
|
||||
shards.splice(idx, 1)
|
||||
}
|
||||
// All shards are down. Return the original preference.
|
||||
return Settings.apis.clsiCache.shards[preferredShard]
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,8 +130,9 @@ function notifyCLSICacheAboutBuild({
|
||||
}) {
|
||||
if (!Settings.apis.clsiCache.enabled) return undefined
|
||||
if (!OBJECT_ID_REGEX.test(projectId)) return undefined
|
||||
const { url, shard } = getShard(projectId)
|
||||
if (isCircuitBreakerTripped(url)) return undefined
|
||||
const shardCfg = getAvailableShard(projectId)
|
||||
if (!shardCfg) return undefined
|
||||
const { url, shard } = shardCfg
|
||||
|
||||
/**
|
||||
* @param {{path: string}[]} files
|
||||
@@ -284,8 +286,9 @@ async function downloadOutputDotSynctexFromCompileCache(
|
||||
) {
|
||||
if (!Settings.apis.clsiCache.enabled) return false
|
||||
if (!OBJECT_ID_REGEX.test(projectId)) return false
|
||||
const { url } = getShard(projectId)
|
||||
if (isCircuitBreakerTripped(url)) return false
|
||||
const shardCfg = getAvailableShard(projectId)
|
||||
if (!shardCfg) return false
|
||||
const { url } = shardCfg
|
||||
|
||||
const timer = new Metrics.Timer(
|
||||
'clsi_cache_download',
|
||||
@@ -347,8 +350,9 @@ async function downloadOutputDotSynctexFromCompileCache(
|
||||
async function downloadLatestCompileCache(projectId, userId, compileDir) {
|
||||
if (!Settings.apis.clsiCache.enabled) return false
|
||||
if (!OBJECT_ID_REGEX.test(projectId)) return false
|
||||
const { url } = getShard(projectId)
|
||||
if (isCircuitBreakerTripped(url)) return false
|
||||
const shardCfg = getAvailableShard(projectId)
|
||||
if (!shardCfg) return false
|
||||
const { url } = shardCfg
|
||||
|
||||
const timer = new Metrics.Timer(
|
||||
'clsi_cache_download',
|
||||
|
||||
@@ -59,7 +59,7 @@ module.exports = {
|
||||
clsiCache: {
|
||||
enabled: !!process.env.CLSI_CACHE_INSTANCES,
|
||||
shards: JSON.parse(process.env.CLSI_CACHE_INSTANCES || '[]').filter(
|
||||
({ zone }) => zone === process.env.ZONE
|
||||
({ zone, readOnly }) => zone === process.env.ZONE && !readOnly
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user