mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-17 07:40:06 +08:00
perf: bound runtime memory and storage usage
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs/promises');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
const {
|
||||
getDirectorySize,
|
||||
normalizeRetentionPolicy,
|
||||
} = require('../../back/shared/retention');
|
||||
|
||||
test('retention policy defaults to disabled and clamps invalid values', () => {
|
||||
assert.deepEqual(normalizeRetentionPolicy({}), {
|
||||
runningInstanceRetentionDays: 0,
|
||||
cronStatRetentionDays: 0,
|
||||
});
|
||||
assert.deepEqual(
|
||||
normalizeRetentionPolicy({
|
||||
runningInstanceRetentionDays: -10,
|
||||
cronStatRetentionDays: 99999,
|
||||
}),
|
||||
{
|
||||
runningInstanceRetentionDays: 0,
|
||||
cronStatRetentionDays: 3650,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('directory preview counts files recursively and ignores symlinks', async (t) => {
|
||||
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'ql-retention-'));
|
||||
const nested = path.join(directory, 'nested');
|
||||
t.after(() => fs.rm(directory, { recursive: true, force: true }));
|
||||
await fs.mkdir(nested);
|
||||
await fs.writeFile(path.join(directory, 'one'), Buffer.alloc(7));
|
||||
await fs.writeFile(path.join(nested, 'two'), Buffer.alloc(11));
|
||||
await fs.symlink(path.join(nested, 'two'), path.join(directory, 'link'));
|
||||
|
||||
assert.equal(await getDirectorySize(directory), 18);
|
||||
assert.equal(await getDirectorySize(path.join(directory, 'missing')), 0);
|
||||
});
|
||||
|
||||
test('cleanup previews first, protects running instances, and uses explicit options', async (t) => {
|
||||
const directory = await fs.mkdtemp(path.join(os.tmpdir(), 'ql-retention-'));
|
||||
const nodeCache = path.join(directory, 'node');
|
||||
await fs.mkdir(nodeCache);
|
||||
await fs.writeFile(path.join(nodeCache, 'cache'), Buffer.alloc(13));
|
||||
|
||||
const config = require('../../back/config').default;
|
||||
const originalCachePath = config.dependenceCachePath;
|
||||
const RunningInstanceModel = {};
|
||||
const CrontabStatModel = {};
|
||||
const InstanceStatus = { running: 0 };
|
||||
const sequelize = {};
|
||||
const stubModule = (modulePath, exports) => {
|
||||
require.cache[require.resolve(modulePath)] = {
|
||||
id: require.resolve(modulePath),
|
||||
filename: require.resolve(modulePath),
|
||||
loaded: true,
|
||||
exports,
|
||||
children: [],
|
||||
paths: [],
|
||||
};
|
||||
};
|
||||
stubModule('../../back/data', { sequelize });
|
||||
stubModule('../../back/data/cronStats', { CrontabStatModel });
|
||||
stubModule('../../back/data/runningInstance', {
|
||||
InstanceStatus,
|
||||
RunningInstanceModel,
|
||||
});
|
||||
stubModule('../../back/data/system', {
|
||||
AuthDataType: { systemConfig: 'systemConfig' },
|
||||
SystemModel: {},
|
||||
});
|
||||
delete require.cache[require.resolve('../../back/services/retention')];
|
||||
const RetentionService = require('../../back/services/retention').default;
|
||||
|
||||
t.after(async () => {
|
||||
config.dependenceCachePath = originalCachePath;
|
||||
await fs.rm(directory, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
let instanceWhere;
|
||||
let vacuumed = false;
|
||||
let instanceCountCalls = 0;
|
||||
let statCountCalls = 0;
|
||||
config.dependenceCachePath = directory;
|
||||
RunningInstanceModel.count = async ({ where }) => {
|
||||
instanceCountCalls++;
|
||||
instanceWhere = where;
|
||||
return 2;
|
||||
};
|
||||
RunningInstanceModel.destroy = async ({ where }) => {
|
||||
instanceWhere = where;
|
||||
return 2;
|
||||
};
|
||||
CrontabStatModel.count = async () => {
|
||||
statCountCalls++;
|
||||
return 3;
|
||||
};
|
||||
CrontabStatModel.destroy = async () => 3;
|
||||
sequelize.transaction = async (callback) => callback({});
|
||||
sequelize.query = async (query) => {
|
||||
vacuumed = query === 'VACUUM';
|
||||
return [];
|
||||
};
|
||||
|
||||
const service = new RetentionService();
|
||||
const disabledPreview = await service.preview({
|
||||
runningInstanceRetentionDays: 0,
|
||||
cronStatRetentionDays: 0,
|
||||
});
|
||||
assert.equal(disabledPreview.runningInstances, 0);
|
||||
assert.equal(disabledPreview.cronStats, 0);
|
||||
assert.equal(instanceCountCalls, 0);
|
||||
assert.equal(statCountCalls, 0);
|
||||
|
||||
const result = await service.cleanup({
|
||||
runningInstanceRetentionDays: 30,
|
||||
cronStatRetentionDays: 90,
|
||||
dependenceCacheTypes: ['node'],
|
||||
compactDatabase: true,
|
||||
});
|
||||
|
||||
assert.equal(instanceWhere.status[Op.ne], InstanceStatus.running);
|
||||
assert.equal(result.deleted.runningInstances, 2);
|
||||
assert.equal(result.deleted.cronStats, 3);
|
||||
assert.equal(result.deleted.dependenceCacheBytes, 13);
|
||||
assert.equal(vacuumed, true);
|
||||
await assert.rejects(fs.stat(nodeCache), { code: 'ENOENT' });
|
||||
});
|
||||
Reference in New Issue
Block a user