mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 13:27:46 +00:00
Merge pull request #4399 from Infisical/audit-log-transaction-fix
fix(audit-logs): move prune audit log transaction inside while loop
This commit is contained in:
@@ -14,7 +14,7 @@ import { ActorType } from "@app/services/auth/auth-type";
|
|||||||
import { EventType, filterableSecretEvents } from "./audit-log-types";
|
import { EventType, filterableSecretEvents } from "./audit-log-types";
|
||||||
|
|
||||||
export interface TAuditLogDALFactory extends Omit<TOrmify<TableName.AuditLog>, "find"> {
|
export interface TAuditLogDALFactory extends Omit<TOrmify<TableName.AuditLog>, "find"> {
|
||||||
pruneAuditLog: (tx?: knex.Knex) => Promise<void>;
|
pruneAuditLog: () => Promise<void>;
|
||||||
find: (
|
find: (
|
||||||
arg: Omit<TFindQuery, "actor" | "eventType"> & {
|
arg: Omit<TFindQuery, "actor" | "eventType"> & {
|
||||||
actorId?: string | undefined;
|
actorId?: string | undefined;
|
||||||
@@ -41,6 +41,10 @@ type TFindQuery = {
|
|||||||
offset?: number;
|
offset?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const QUERY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
||||||
|
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
|
||||||
|
const MAX_RETRY_ON_FAILURE = 3;
|
||||||
|
|
||||||
export const auditLogDALFactory = (db: TDbClient) => {
|
export const auditLogDALFactory = (db: TDbClient) => {
|
||||||
const auditLogOrm = ormify(db, TableName.AuditLog);
|
const auditLogOrm = ormify(db, TableName.AuditLog);
|
||||||
|
|
||||||
@@ -151,20 +155,20 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// delete all audit log that have expired
|
// delete all audit log that have expired
|
||||||
const pruneAuditLog: TAuditLogDALFactory["pruneAuditLog"] = async (tx) => {
|
const pruneAuditLog: TAuditLogDALFactory["pruneAuditLog"] = async () => {
|
||||||
const runPrune = async (dbClient: knex.Knex) => {
|
const today = new Date();
|
||||||
const AUDIT_LOG_PRUNE_BATCH_SIZE = 10000;
|
let deletedAuditLogIds: { id: string }[] = [];
|
||||||
const MAX_RETRY_ON_FAILURE = 3;
|
let numberOfRetryOnFailure = 0;
|
||||||
|
let isRetrying = false;
|
||||||
|
|
||||||
const today = new Date();
|
logger.info(`${QueueName.DailyResourceCleanUp}: audit log started`);
|
||||||
let deletedAuditLogIds: { id: string }[] = [];
|
do {
|
||||||
let numberOfRetryOnFailure = 0;
|
try {
|
||||||
let isRetrying = false;
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
deletedAuditLogIds = await db.transaction(async (trx) => {
|
||||||
|
await trx.raw(`SET statement_timeout = ${QUERY_TIMEOUT_MS}`);
|
||||||
|
|
||||||
logger.info(`${QueueName.DailyResourceCleanUp}: audit log started`);
|
const findExpiredLogSubQuery = trx(TableName.AuditLog)
|
||||||
do {
|
|
||||||
try {
|
|
||||||
const findExpiredLogSubQuery = dbClient(TableName.AuditLog)
|
|
||||||
.where("expiresAt", "<", today)
|
.where("expiresAt", "<", today)
|
||||||
.where("createdAt", "<", today) // to use audit log partition
|
.where("createdAt", "<", today) // to use audit log partition
|
||||||
.orderBy(`${TableName.AuditLog}.createdAt`, "desc")
|
.orderBy(`${TableName.AuditLog}.createdAt`, "desc")
|
||||||
@@ -172,35 +176,25 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
|
.limit(AUDIT_LOG_PRUNE_BATCH_SIZE);
|
||||||
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
deletedAuditLogIds = await dbClient(TableName.AuditLog)
|
const results = await trx(TableName.AuditLog).whereIn("id", findExpiredLogSubQuery).del().returning("id");
|
||||||
.whereIn("id", findExpiredLogSubQuery)
|
|
||||||
.del()
|
|
||||||
.returning("id");
|
|
||||||
numberOfRetryOnFailure = 0; // reset
|
|
||||||
} catch (error) {
|
|
||||||
numberOfRetryOnFailure += 1;
|
|
||||||
deletedAuditLogIds = [];
|
|
||||||
logger.error(error, "Failed to delete audit log on pruning");
|
|
||||||
} finally {
|
|
||||||
// eslint-disable-next-line no-await-in-loop
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
setTimeout(resolve, 10); // time to breathe for db
|
|
||||||
});
|
|
||||||
}
|
|
||||||
isRetrying = numberOfRetryOnFailure > 0;
|
|
||||||
} while (deletedAuditLogIds.length > 0 || (isRetrying && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE));
|
|
||||||
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (tx) {
|
return results;
|
||||||
await runPrune(tx);
|
});
|
||||||
} else {
|
|
||||||
const QUERY_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
numberOfRetryOnFailure = 0; // reset
|
||||||
await db.transaction(async (trx) => {
|
} catch (error) {
|
||||||
await trx.raw(`SET statement_timeout = ${QUERY_TIMEOUT_MS}`);
|
numberOfRetryOnFailure += 1;
|
||||||
await runPrune(trx);
|
deletedAuditLogIds = [];
|
||||||
});
|
logger.error(error, "Failed to delete audit log on pruning");
|
||||||
}
|
} finally {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, 10); // time to breathe for db
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isRetrying = numberOfRetryOnFailure > 0;
|
||||||
|
} while (deletedAuditLogIds.length > 0 || (isRetrying && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE));
|
||||||
|
logger.info(`${QueueName.DailyResourceCleanUp}: audit log completed`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const create: TAuditLogDALFactory["create"] = async (tx) => {
|
const create: TAuditLogDALFactory["create"] = async (tx) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user