fix(backend): 処理に失敗するとidempotentキーを削除してしまいその後のリクエストが通ってしまう問題を修正 (MisskeyIO#591)
This commit is contained in:
		
							parent
							
								
									92bdc2e054
								
							
						
					
					
						commit
						31ebd77e8a
					
				|  | @ -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 = { | ||||
| 	provide: DI.redis, | ||||
| 	useFactory: (config: Config) => { | ||||
| 		return new Redis.Redis({ | ||||
| 		const redis = new Redis.Redis({ | ||||
| 			...config.redis, | ||||
| 			reconnectOnError: (err: Error) => { | ||||
| 				if ( err.message.includes('READONLY') | ||||
|  | @ -57,6 +57,27 @@ const $redis: Provider = { | |||
| 				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], | ||||
| }; | ||||
|  | @ -101,7 +122,7 @@ const $redisForSub: Provider = { | |||
| const $redisForTimelines: Provider = { | ||||
| 	provide: DI.redisForTimelines, | ||||
| 	useFactory: (config: Config) => { | ||||
| 		return new Redis.Redis({ | ||||
| 		const redis = new Redis.Redis({ | ||||
| 			...config.redisForTimelines, | ||||
| 			reconnectOnError: (err: Error) => { | ||||
| 				if ( err.message.includes('READONLY') | ||||
|  | @ -111,6 +132,27 @@ const $redisForTimelines: Provider = { | |||
| 				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], | ||||
| }; | ||||
|  |  | |||
|  | @ -173,8 +173,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | |||
| 				logger.info('Successfully created drive file.', { fileId: driveFile.id }); | ||||
| 				return await this.driveFileEntityService.pack(driveFile, me, { self: true }); | ||||
| 			} 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 }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -117,8 +117,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | |||
| 					}); | ||||
| 				}, | ||||
| 				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 }); | ||||
| 				}, | ||||
| 			); | ||||
|  |  | |||
|  | @ -445,8 +445,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- | |||
| 					createdNote: await this.noteEntityService.pack(note, me), | ||||
| 				}; | ||||
| 			} 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 }); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue