fix(backend): 処理に失敗するとidempotentキーを削除してしまいその後のリクエストが通ってしまう問題を修正 (MisskeyIO#591)
This commit is contained in:
parent
92bdc2e054
commit
31ebd77e8a
29
packages/backend/src/@types/ioredis.d.ts
vendored
Normal file
29
packages/backend/src/@types/ioredis.d.ts
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Result, Callback } from 'ioredis';
|
||||||
|
|
||||||
|
declare module 'ioredis' {
|
||||||
|
interface RedisCommander<Context> {
|
||||||
|
/*
|
||||||
|
* Set value if key has the specified value.
|
||||||
|
*
|
||||||
|
* lua script:
|
||||||
|
* if redis.call('GET', KEYS[1]) == ARGV[1] then
|
||||||
|
* return redis.call('SET', KEYS[1], ARGV[2])
|
||||||
|
* else
|
||||||
|
* return 0
|
||||||
|
* end
|
||||||
|
*/
|
||||||
|
setIf(key: string, value: string, newValue: string, callback?: Callback<string>): Result<string, Context>;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlink key if key has the specified value.
|
||||||
|
*
|
||||||
|
* lua script:
|
||||||
|
* if redis.call('GET', KEYS[1]) == ARGV[1] then
|
||||||
|
* return redis.call('UNLINK', KEYS[1])
|
||||||
|
* else
|
||||||
|
* return 0
|
||||||
|
* end
|
||||||
|
*/
|
||||||
|
unlinkIf(key: string, value: string, callback?: Callback<string>): Result<string, Context>;
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ const $meilisearch: Provider = {
|
||||||
const $redis: Provider = {
|
const $redis: Provider = {
|
||||||
provide: DI.redis,
|
provide: DI.redis,
|
||||||
useFactory: (config: Config) => {
|
useFactory: (config: Config) => {
|
||||||
return new Redis.Redis({
|
const redis = new Redis.Redis({
|
||||||
...config.redis,
|
...config.redis,
|
||||||
reconnectOnError: (err: Error) => {
|
reconnectOnError: (err: Error) => {
|
||||||
if ( err.message.includes('READONLY')
|
if ( err.message.includes('READONLY')
|
||||||
|
@ -57,6 +57,27 @@ const $redis: Provider = {
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
redis.defineCommand('setIf', {
|
||||||
|
numberOfKeys: 1,
|
||||||
|
lua: `
|
||||||
|
if redis.call('GET', KEYS[1]) == ARGV[1] then
|
||||||
|
return redis.call('SET', KEYS[1], ARGV[2])
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
redis.defineCommand('unlinkIf', {
|
||||||
|
numberOfKeys: 1,
|
||||||
|
lua: `
|
||||||
|
if redis.call('GET', KEYS[1]) == ARGV[1] then
|
||||||
|
return redis.call('UNLINK', KEYS[1])
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
return redis;
|
||||||
},
|
},
|
||||||
inject: [DI.config],
|
inject: [DI.config],
|
||||||
};
|
};
|
||||||
|
@ -101,7 +122,7 @@ const $redisForSub: Provider = {
|
||||||
const $redisForTimelines: Provider = {
|
const $redisForTimelines: Provider = {
|
||||||
provide: DI.redisForTimelines,
|
provide: DI.redisForTimelines,
|
||||||
useFactory: (config: Config) => {
|
useFactory: (config: Config) => {
|
||||||
return new Redis.Redis({
|
const redis = new Redis.Redis({
|
||||||
...config.redisForTimelines,
|
...config.redisForTimelines,
|
||||||
reconnectOnError: (err: Error) => {
|
reconnectOnError: (err: Error) => {
|
||||||
if ( err.message.includes('READONLY')
|
if ( err.message.includes('READONLY')
|
||||||
|
@ -111,6 +132,27 @@ const $redisForTimelines: Provider = {
|
||||||
return 1;
|
return 1;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
redis.defineCommand('setIf', {
|
||||||
|
numberOfKeys: 1,
|
||||||
|
lua: `
|
||||||
|
if redis.call('GET', KEYS[1]) == ARGV[1] then
|
||||||
|
return redis.call('SET', KEYS[1], ARGV[2])
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
redis.defineCommand('unlinkIf', {
|
||||||
|
numberOfKeys: 1,
|
||||||
|
lua: `
|
||||||
|
if redis.call('GET', KEYS[1]) == ARGV[1] then
|
||||||
|
return redis.call('UNLINK', KEYS[1])
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
return redis;
|
||||||
},
|
},
|
||||||
inject: [DI.config],
|
inject: [DI.config],
|
||||||
};
|
};
|
||||||
|
|
|
@ -173,8 +173,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
logger.info('Successfully created drive file.', { fileId: driveFile.id });
|
logger.info('Successfully created drive file.', { fileId: driveFile.id });
|
||||||
return await this.driveFileEntityService.pack(driveFile, me, { self: true });
|
return await this.driveFileEntityService.pack(driveFile, me, { self: true });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// エラーが発生した場合、リクエストの処理結果を削除
|
// エラーが発生した場合、まだ処理中として記録されている場合はリクエストの処理結果を削除
|
||||||
await this.redisClient.unlink(`drive:files:create:idempotent:${me.id}:${hash}`);
|
await this.redisClient.unlinkIf(`drive:files:create:idempotent:${me.id}:${hash}`, '_');
|
||||||
|
|
||||||
logger.error('Failed to create drive file.', { error: e });
|
logger.error('Failed to create drive file.', { error: e });
|
||||||
|
|
||||||
|
|
|
@ -117,8 +117,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async err => {
|
async err => {
|
||||||
// エラーが発生した場合、リクエストの処理結果を削除
|
// エラーが発生した場合、まだ処理中として記録されている場合はリクエストの処理結果を削除
|
||||||
await this.redisClient.unlink(`drive:files:upload-from-url:idempotent:${me.id}:${hash}`);
|
await this.redisClient.unlinkIf(`drive:files:upload-from-url:idempotent:${me.id}:${hash}`, '_');
|
||||||
logger.error('Failed to upload from URL.', { error: err });
|
logger.error('Failed to upload from URL.', { error: err });
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -445,8 +445,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
createdNote: await this.noteEntityService.pack(note, me),
|
createdNote: await this.noteEntityService.pack(note, me),
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// エラーが発生した場合、リクエストの処理結果を削除
|
// エラーが発生した場合、まだ処理中として記録されている場合はリクエストの処理結果を削除
|
||||||
await this.redisForTimelines.unlink(`note:idempotent:${me.id}:${hash}`);
|
await this.redisForTimelines.unlinkIf(`note:idempotent:${me.id}:${hash}`, '_');
|
||||||
|
|
||||||
logger.error('Failed to create a note.', { error: err });
|
logger.error('Failed to create a note.', { error: err });
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue