Setup and migrate to Prisma (#456)

This commit is contained in:
Peerawas Archavanuntakun 2022-01-06 03:30:32 +07:00 committed by GitHub
parent 129d121364
commit 51d378e4cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 605 additions and 273 deletions

View file

@ -1,4 +1,5 @@
import dotenv from 'dotenv';
import 'reflect-metadata';
import {injectable} from 'inversify';
import path from 'path';
import xbytes from 'xbytes';

View file

@ -1,12 +1,12 @@
import {promises as fs, createWriteStream} from 'fs';
import path from 'path';
import {inject, injectable} from 'inversify';
import sequelize from 'sequelize';
import {FileCache} from '../models/index.js';
import {TYPES} from '../types.js';
import Config from './config.js';
import PQueue from 'p-queue';
import debug from '../utils/debug.js';
import {prisma} from '../utils/db.js';
import {FileCache} from '@prisma/client';
@injectable()
export default class FileCacheProvider {
@ -23,7 +23,11 @@ export default class FileCacheProvider {
* @param hash lookup key
*/
async getPathFor(hash: string): Promise<string> {
const model = await FileCache.findByPk(hash);
const model = await prisma.fileCache.findUnique({
where: {
hash,
},
});
if (!model) {
throw new Error('File is not cached');
@ -34,12 +38,23 @@ export default class FileCacheProvider {
try {
await fs.access(resolvedPath);
} catch (_: unknown) {
await FileCache.destroy({where: {hash}});
await prisma.fileCache.delete({
where: {
hash,
},
});
throw new Error('File is not cached');
}
await model.update({accessedAt: new Date()});
await prisma.fileCache.update({
where: {
hash,
},
data: {
accessedAt: new Date(),
},
});
return resolvedPath;
}
@ -64,7 +79,13 @@ export default class FileCacheProvider {
try {
await fs.rename(tmpPath, finalPath);
await FileCache.create({hash, bytes: stats.size, accessedAt: new Date()});
await prisma.fileCache.create({
data: {
hash,
accessedAt: new Date(),
bytes: stats.size,
},
});
} catch (error) {
debug('Errored when moving a finished cache file:', error);
}
@ -100,14 +121,19 @@ export default class FileCacheProvider {
// Continue to evict until we're under the limit
/* eslint-disable no-await-in-loop */
while (totalSizeBytes > this.config.CACHE_LIMIT_IN_BYTES) {
const oldest = await FileCache.findOne({
order: [
['accessedAt', 'ASC'],
],
const oldest = await prisma.fileCache.findFirst({
orderBy: {
accessedAt: 'asc',
},
});
if (oldest) {
await oldest.destroy();
await prisma.fileCache.delete({
where: {
hash: oldest.hash,
},
});
await fs.unlink(path.join(this.config.CACHE_DIR, oldest.hash));
debug(`${oldest.hash} has been evicted`);
numOfEvictedFiles++;
@ -128,7 +154,11 @@ export default class FileCacheProvider {
// Check filesystem direction (do files exist on the disk but not in the database?)
for await (const dirent of await fs.opendir(this.config.CACHE_DIR)) {
if (dirent.isFile()) {
const model = await FileCache.findByPk(dirent.name);
const model = await prisma.fileCache.findUnique({
where: {
hash: dirent.name,
},
});
if (!model) {
debug(`${dirent.name} was present on disk but was not in the database. Removing from disk.`);
@ -145,7 +175,11 @@ export default class FileCacheProvider {
await fs.access(filePath);
} catch {
debug(`${model.hash} was present in database but was not on disk. Removing from database.`);
await model.destroy();
await prisma.fileCache.delete({
where: {
hash: model.hash,
},
});
}
}
}
@ -156,11 +190,12 @@ export default class FileCacheProvider {
* @returns the total size of the cache in bytes
*/
private async getDiskUsageInBytes() {
const [{dataValues: {totalSizeBytes}}] = await FileCache.findAll({
attributes: [
[sequelize.fn('sum', sequelize.col('bytes')), 'totalSizeBytes'],
],
}) as unknown as [{dataValues: {totalSizeBytes: number}}];
const data = await prisma.fileCache.aggregate({
_sum: {
bytes: true,
},
});
const totalSizeBytes = data._sum.bytes ?? 0;
return totalSizeBytes;
}
@ -176,24 +211,26 @@ export default class FileCacheProvider {
let models: FileCache[] = [];
const fetchNextBatch = async () => {
let where = {};
let where;
if (previousCreatedAt) {
where = {
createdAt: {
[sequelize.Op.gt]: previousCreatedAt,
gt: previousCreatedAt,
},
};
}
models = await FileCache.findAll({
models = await prisma.fileCache.findMany({
where,
limit,
order: ['createdAt'],
orderBy: {
createdAt: 'asc',
},
take: limit,
});
if (models.length > 0) {
previousCreatedAt = models[models.length - 1].createdAt as Date;
previousCreatedAt = models[models.length - 1].createdAt;
}
};

View file

@ -1,5 +1,5 @@
import {injectable} from 'inversify';
import {KeyValueCache} from '../models/index.js';
import {prisma} from '../utils/db.js';
import debug from '../utils/debug.js';
type Seconds = number;
@ -29,7 +29,11 @@ export default class KeyValueCacheProvider {
throw new Error(`Cache key ${key} is too short.`);
}
const cachedResult = await KeyValueCache.findByPk(key);
const cachedResult = await prisma.keyValueCache.findUnique({
where: {
key,
},
});
if (cachedResult) {
if (new Date() < cachedResult.expiresAt) {
@ -37,7 +41,11 @@ export default class KeyValueCacheProvider {
return JSON.parse(cachedResult.value) as F;
}
await cachedResult.destroy();
await prisma.keyValueCache.delete({
where: {
key,
},
});
}
debug(`Cache miss: ${key}`);
@ -45,10 +53,21 @@ export default class KeyValueCacheProvider {
const result = await func(...options as any[]);
// Save result
await KeyValueCache.upsert({
key,
value: JSON.stringify(result),
expiresAt: futureTimeToDate(expiresIn),
const value = JSON.stringify(result);
const expiresAt = futureTimeToDate(expiresIn);
await prisma.keyValueCache.upsert({
where: {
key,
},
update: {
value,
expiresAt,
},
create: {
key,
value,
expiresAt,
},
});
return result;