mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-19 23:37:15 +08:00
Revert "fix(server): wrap read-modify-write apis with distributed lock (#5979)"
This reverts commit 34f892b05b.
This commit is contained in:
@@ -1,2 +1 @@
|
||||
export * from './payment-required';
|
||||
export * from './too-many-requests';
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
|
||||
export class TooManyRequestsException extends HttpException {
|
||||
constructor(desc?: string, code: string = 'Too Many Requests') {
|
||||
super(
|
||||
HttpException.createBody(
|
||||
desc ?? code,
|
||||
code,
|
||||
HttpStatus.TOO_MANY_REQUESTS
|
||||
),
|
||||
HttpStatus.TOO_MANY_REQUESTS
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,6 @@ import { GraphQLError } from 'graphql';
|
||||
import { Config } from '../config';
|
||||
import { GQLLoggerPlugin } from './logger-plugin';
|
||||
|
||||
export type GraphqlContext = {
|
||||
req: Request;
|
||||
res: Response;
|
||||
isAdminQuery: boolean;
|
||||
};
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -36,13 +30,7 @@ export type GraphqlContext = {
|
||||
: '../../../schema.gql'
|
||||
),
|
||||
sortSchema: true,
|
||||
context: ({
|
||||
req,
|
||||
res,
|
||||
}: {
|
||||
req: Request;
|
||||
res: Response;
|
||||
}): GraphqlContext => ({
|
||||
context: ({ req, res }: { req: Request; res: Response }) => ({
|
||||
req,
|
||||
res,
|
||||
isAdminQuery: false,
|
||||
|
||||
@@ -14,23 +14,14 @@ export {
|
||||
} from './config';
|
||||
export * from './error';
|
||||
export { EventEmitter, type EventPayload, OnEvent } from './event';
|
||||
export type { GraphqlContext } from './graphql';
|
||||
export { CryptoHelper, URLHelper } from './helpers';
|
||||
export { MailService } from './mailer';
|
||||
export { CallCounter, CallTimer, metrics } from './metrics';
|
||||
export {
|
||||
BucketService,
|
||||
LockGuard,
|
||||
MUTEX_RETRY,
|
||||
MUTEX_WAIT,
|
||||
MutexService,
|
||||
} from './mutex';
|
||||
export {
|
||||
getOptionalModuleMetadata,
|
||||
GlobalExceptionFilter,
|
||||
OptionalModule,
|
||||
} from './nestjs';
|
||||
export type { PrismaTransaction } from './prisma';
|
||||
export * from './storage';
|
||||
export { type StorageProvider, StorageProviderFactory } from './storage';
|
||||
export { AuthThrottlerGuard, CloudThrottlerGuard, Throttle } from './throttler';
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
export class BucketService {
|
||||
private readonly bucket = new Map<string, string>();
|
||||
|
||||
get(key: string) {
|
||||
return this.bucket.get(key);
|
||||
}
|
||||
|
||||
set(key: string, value: string) {
|
||||
this.bucket.set(key, value);
|
||||
}
|
||||
|
||||
delete(key: string) {
|
||||
this.bucket.delete(key);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
|
||||
import { BucketService } from './bucket';
|
||||
import { MutexService } from './mutex';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [BucketService, MutexService],
|
||||
exports: [BucketService, MutexService],
|
||||
})
|
||||
export class MutexModule {}
|
||||
|
||||
export { BucketService, MutexService };
|
||||
export { LockGuard, MUTEX_RETRY, MUTEX_WAIT } from './mutex';
|
||||
@@ -1,96 +0,0 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
|
||||
import { Inject, Injectable, Logger, Scope } from '@nestjs/common';
|
||||
import { CONTEXT } from '@nestjs/graphql';
|
||||
|
||||
import type { GraphqlContext } from '../graphql';
|
||||
import { BucketService } from './bucket';
|
||||
|
||||
export class LockGuard<M extends MutexService = MutexService>
|
||||
implements AsyncDisposable
|
||||
{
|
||||
constructor(
|
||||
private readonly mutex: M,
|
||||
private readonly key: string
|
||||
) {}
|
||||
|
||||
async [Symbol.asyncDispose]() {
|
||||
return this.mutex.unlock(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
export const MUTEX_RETRY = 5;
|
||||
export const MUTEX_WAIT = 100;
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class MutexService {
|
||||
protected logger = new Logger(MutexService.name);
|
||||
|
||||
constructor(
|
||||
@Inject(CONTEXT) private readonly context: GraphqlContext,
|
||||
private readonly bucket: BucketService
|
||||
) {}
|
||||
|
||||
protected getId() {
|
||||
let id = this.context.req.headers['x-transaction-id'] as string;
|
||||
|
||||
if (!id) {
|
||||
id = randomUUID();
|
||||
this.context.req.headers['x-transaction-id'] = id;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* lock an resource and return a lock guard, which will release the lock when disposed
|
||||
*
|
||||
* if the lock is not available, it will retry for [MUTEX_RETRY] times
|
||||
*
|
||||
* usage:
|
||||
* ```typescript
|
||||
* {
|
||||
* // lock is acquired here
|
||||
* await using lock = await mutex.lock('resource-key');
|
||||
* if (lock) {
|
||||
* // do something
|
||||
* } else {
|
||||
* // failed to lock
|
||||
* }
|
||||
* }
|
||||
* // lock is released here
|
||||
* ```
|
||||
* @param key resource key
|
||||
* @returns LockGuard
|
||||
*/
|
||||
async lock(key: string): Promise<LockGuard | undefined> {
|
||||
const id = this.getId();
|
||||
const fetchLock = async (retry: number): Promise<LockGuard | undefined> => {
|
||||
if (retry === 0) {
|
||||
this.logger.error(
|
||||
`Failed to fetch lock ${key} after ${MUTEX_RETRY} retry`
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
const current = this.bucket.get(key);
|
||||
if (current && current !== id) {
|
||||
this.logger.warn(
|
||||
`Failed to fetch lock ${key}, retrying in ${MUTEX_WAIT} ms`
|
||||
);
|
||||
await setTimeout(MUTEX_WAIT * (MUTEX_RETRY - retry + 1));
|
||||
return fetchLock(retry - 1);
|
||||
}
|
||||
this.bucket.set(key, id);
|
||||
return new LockGuard(this, key);
|
||||
};
|
||||
|
||||
return fetchLock(MUTEX_RETRY);
|
||||
}
|
||||
|
||||
async unlock(key: string): Promise<void> {
|
||||
if (this.bucket.get(key) === this.getId()) {
|
||||
this.bucket.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,3 @@ const clientProvider: Provider = {
|
||||
})
|
||||
export class PrismaModule {}
|
||||
export { PrismaService } from './service';
|
||||
|
||||
export type PrismaTransaction = Parameters<
|
||||
Parameters<PrismaClient['$transaction']>[0]
|
||||
>[0];
|
||||
|
||||
Reference in New Issue
Block a user