// @ts-nocheck import * as iam from '@ownzones/iam-service'; import * as commander from 'commander'; import * as redis from 'redis'; import * as bluebird from 'bluebird'; import { config } from '../../config'; bluebird.promisifyAll(redis.RedisClient.prototype); bluebird.promisifyAll(redis.Multi.prototype); const redisConfig = { host: commander.host || config.cacheRedis.host, port: commander.port || '6379', db: parseInt(commander.db, 10) || 7, } as any; if (process.env.FORCE_REDIS_SSL && process.env.FORCE_REDIS_SSL.toLowerCase() === 'true') { redisConfig.tls = {}; } const redisClient = redis.createClient(redisConfig) as any; function organizationLRU(organization: string): string { return `${config.cachePrefix}:${organization}:LRU`; } function keyLRU(key: string): string { return organizationLRU(key.split(':')[1]); } interface IOptions { verbose: boolean; } async function run(options: IOptions) { const organizations = (await iam.getAllOrganizations(iam.setContext())).map(org => org.slug); const orgStorage = organizations.map(org => 0); // Keys not found in LRU for a specific organization const extraKeys = organizations.map(org => 0); console.log(`Checking the following organizations: ${organizations}`); let cursor; let redisMutating = false; for (const organization of organizations) { console.log(`Processing organization ${organization}`); // Get the segments in LRU for each organization const orgLRU = organizationLRU(organization); cursor = '0'; let inconsistencies = 0; do { const [nextCursor, segments] = await redisClient.zscanAsync(orgLRU, cursor, 'MATCH', '*', 'COUNT', 1000); const segmentIds = []; segments.forEach((s, i) => { if (i % 2 === 0) segmentIds.push(s); }); const multi = redisClient.multi(); segmentIds.forEach(s => multi.exists(s)); segmentIds.forEach(s => multi.zscore(orgLRU, s)); const result = await multi.execAsync(); for (let i = 0; i < segmentIds.length; i += 1) { // Check if the existence of the segment in LRU is the same in the keys const inKeys = (result[i] === 1); const inLRU = (result[i + segmentIds.length] != null); if (inLRU && !inKeys) { inconsistencies += 1; if (options.verbose) { console.log(`[ERROR] Inconsistency found for segment ${segmentIds[i]}: in LRU: ${inLRU}, in keys: ${inKeys}`); } } if (!inLRU && !inKeys) { redisMutating = true; } } cursor = nextCursor; } while (cursor !== '0'); console.log(`Found ${inconsistencies} more segments in LRU for the organization ${organization}`); console.log(`Done with LRU for the organization ${organization}\n`); } console.log('Processing all the keys'); cursor = '0'; do { const r = await redisClient.scanAsync(cursor, 'MATCH', `${config.cachePrefix}:*:*`, 'COUNT', 2500); const nextCursor = r[0]; let keys = r[1]; keys = keys.filter((key) => { const keyTokens = key.split(':'); const id = keyTokens[2]; const organization = keyTokens[1]; if (id === 'maxStorageSize' || id === 'LRU' || id === 'storageSize' || keyTokens.length !== 3) { return false; } if (!organizations.includes(organization)) { return false; } return true; }); const multi = redisClient.multi(); keys.forEach(key => multi.exists(key)); keys.forEach(key => multi.zscore(keyLRU(key), key)); keys.forEach(key => multi.hgetall(key)); const result = await multi.execAsync(); for (let i = 0; i < keys.length; i += 1) { const inKeys = (result[i] === 1); const inLRU = (result[i + keys.length] != null); const organization = keys[i].split(':')[1]; const index = organizations.indexOf(organization); if (inKeys && !inLRU) { if (options.verbose) { console.log(`[ERROR] Inconsistency found for segment ${keys[i]}: in LRU: ${inLRU}, in keys: ${inKeys}`); } extraKeys[organization] += 1; } if (!inLRU && !inKeys) { redisMutating = true; } if (inKeys) { const segment = result[i + 2 * keys.length]; orgStorage[index] += parseInt(segment.size, 10); } } cursor = nextCursor; } while (cursor !== '0'); for (let i = 0; i < organizations.length; i += 1) { const organization = organizations[i]; console.log(`Keys not found in LRU for organization ${organization}: ${extraKeys[i]}`); } if (redisMutating) { console.log('[WARNING] The cache is being modified, the following reported cache inconsistencies might be misleading'); } for (let i = 0; i < organizations.length; i += 1) { const organization = organizations[i]; const computedStorage = orgStorage[i]; const heldStorage = parseInt(await redisClient.getAsync(`${config.cachePrefix}:${organization}:storageSize`) || 0, 10); if (computedStorage !== heldStorage) { console.log(`[ERROR] Inconsistency for organization ${organization}: displayed: ${heldStorage}, computed: ${computedStorage}, difference: ${heldStorage - computedStorage}`); } } console.log('Done!'); await redisClient.quitAsync(); } const command = new commander.Command(); command .option('-v --verbose', 'Display all key inconsistencies', false) .parse(process.argv); run(command as any as IOptions);