overleaf-cep/services/history-v1/storage/scripts/show_buffer.js
Brian Gough d280f40885 Merge pull request #26116 from overleaf/bg-history-redis-show-buffer
add script to display redis buffer for a given history ID

GitOrigin-RevId: 71c2e79480c0873d30801ed3c13aa9a7fc7873f6
2025-06-09 08:05:35 +00:00

117 lines
3.2 KiB
JavaScript

#!/usr/bin/env node
// @ts-check
const { rclientHistory: rclient } = require('../lib/redis')
const { keySchema } = require('../lib/chunk_store/redis')
const commandLineArgs = require('command-line-args')
const optionDefinitions = [
{ name: 'historyId', type: String, defaultOption: true },
]
// Column width for key display alignment; can be overridden with COL_WIDTH env variable
const COLUMN_WIDTH = process.env.COL_WIDTH
? parseInt(process.env.COL_WIDTH, 10)
: 45
let options
try {
options = commandLineArgs(optionDefinitions)
} catch (e) {
console.error(
'Error parsing command line arguments:',
e instanceof Error ? e.message : String(e)
)
console.error('Usage: ./show_buffer.js <historyId>')
process.exit(1)
}
const { historyId } = options
if (!historyId) {
console.error('Usage: ./show_buffer.js <historyId>')
process.exit(1)
}
function format(str, indent = COLUMN_WIDTH + 2) {
const lines = str.split('\n')
for (let i = 1; i < lines.length; i++) {
lines[i] = ' '.repeat(indent) + lines[i]
}
return lines.join('\n')
}
async function displayKeyValue(
rclient,
key,
{ parseJson = false, formatDate = false } = {}
) {
const value = await rclient.get(key)
let displayValue = '(nil)'
if (value) {
if (parseJson) {
try {
displayValue = format(JSON.stringify(JSON.parse(value), null, 2))
} catch (e) {
displayValue = ` Raw value: ${value}`
}
} else if (formatDate) {
const ts = parseInt(value, 10)
displayValue = `${new Date(ts).toISOString()} (${value})`
} else {
displayValue = value
}
}
console.log(`${key.padStart(COLUMN_WIDTH)}: ${displayValue}`)
}
async function displayBuffer(projectId) {
console.log(`Buffer for history ID: ${projectId}`)
console.log('--------------------------------------------------')
try {
const headKey = keySchema.head({ projectId })
const headVersionKey = keySchema.headVersion({ projectId })
const persistedVersionKey = keySchema.persistedVersion({ projectId })
const expireTimeKey = keySchema.expireTime({ projectId })
const persistTimeKey = keySchema.persistTime({ projectId })
const changesKey = keySchema.changes({ projectId })
await displayKeyValue(rclient, headKey, { parseJson: true })
await displayKeyValue(rclient, headVersionKey)
await displayKeyValue(rclient, persistedVersionKey)
await displayKeyValue(rclient, expireTimeKey, { formatDate: true })
await displayKeyValue(rclient, persistTimeKey, { formatDate: true })
const changesList = await rclient.lrange(changesKey, 0, -1)
// 6. changes
let changesListDisplay = '(nil)'
if (changesList) {
changesListDisplay = changesList.length
? format(
changesList
.map((change, index) => `[${index}]: ${change}`)
.join('\n')
)
: '(empty list)'
}
console.log(`${changesKey.padStart(COLUMN_WIDTH)}: ${changesListDisplay}`)
} catch (error) {
console.error('Error fetching data from Redis:', error)
throw error
}
}
;(async () => {
let errorOccurred = false
try {
await displayBuffer(historyId)
} catch (error) {
errorOccurred = true
} finally {
rclient.quit(() => {
process.exit(errorOccurred ? 1 : 0)
})
}
})()