mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 05:24:13 +00:00 
			
		
		
		
	AP Lock (#5410)
This commit is contained in:
		
							parent
							
								
									9b91b92bca
								
							
						
					
					
						commit
						827c378ac1
					
				
					 6 changed files with 100 additions and 60 deletions
				
			
		|  | @ -199,6 +199,7 @@ | ||||||
| 		"recaptcha-promise": "0.1.3", | 		"recaptcha-promise": "0.1.3", | ||||||
| 		"reconnecting-websocket": "4.2.0", | 		"reconnecting-websocket": "4.2.0", | ||||||
| 		"redis": "2.8.0", | 		"redis": "2.8.0", | ||||||
|  | 		"redis-lock": "0.1.4", | ||||||
| 		"reflect-metadata": "0.1.13", | 		"reflect-metadata": "0.1.13", | ||||||
| 		"rename": "1.0.4", | 		"rename": "1.0.4", | ||||||
| 		"request": "2.88.0", | 		"request": "2.88.0", | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								src/misc/app-lock.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/misc/app-lock.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | import redis from '../db/redis'; | ||||||
|  | import { promisify } from 'util'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Retry delay (ms) for lock acquisition | ||||||
|  |  */ | ||||||
|  | const retryDelay = 100; | ||||||
|  | 
 | ||||||
|  | const lock: (key: string, timeout?: number) => Promise<() => void> | ||||||
|  | 	= redis | ||||||
|  | 	? promisify(require('redis-lock')(redis, retryDelay)) | ||||||
|  | 	: async () => () => { }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Get AP Object lock | ||||||
|  |  * @param uri AP object ID | ||||||
|  |  * @param timeout Lock timeout (ms), The timeout releases previous lock. | ||||||
|  |  * @returns Unlock function | ||||||
|  |  */ | ||||||
|  | export function getApLock(uri: string, timeout = 30 * 1000) { | ||||||
|  | 	return lock(`ap-object:${uri}`, timeout); | ||||||
|  | } | ||||||
|  | @ -7,6 +7,7 @@ import { resolvePerson } from '../../models/person'; | ||||||
| import { apLogger } from '../../logger'; | import { apLogger } from '../../logger'; | ||||||
| import { extractDbHost } from '../../../../misc/convert-host'; | import { extractDbHost } from '../../../../misc/convert-host'; | ||||||
| import { fetchMeta } from '../../../../misc/fetch-meta'; | import { fetchMeta } from '../../../../misc/fetch-meta'; | ||||||
|  | import { getApLock } from '../../../../misc/app-lock'; | ||||||
| 
 | 
 | ||||||
| const logger = apLogger; | const logger = apLogger; | ||||||
| 
 | 
 | ||||||
|  | @ -25,6 +26,9 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: | ||||||
| 	const meta = await fetchMeta(); | 	const meta = await fetchMeta(); | ||||||
| 	if (meta.blockedHosts.includes(extractDbHost(uri))) return; | 	if (meta.blockedHosts.includes(extractDbHost(uri))) return; | ||||||
| 
 | 
 | ||||||
|  | 	const unlock = await getApLock(uri); | ||||||
|  | 
 | ||||||
|  | 	try { | ||||||
| 		// 既に同じURIを持つものが登録されていないかチェック
 | 		// 既に同じURIを持つものが登録されていないかチェック
 | ||||||
| 		const exist = await fetchNote(uri); | 		const exist = await fetchNote(uri); | ||||||
| 		if (exist) { | 		if (exist) { | ||||||
|  | @ -66,6 +70,9 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: | ||||||
| 			visibleUsers, | 			visibleUsers, | ||||||
| 			uri | 			uri | ||||||
| 		}); | 		}); | ||||||
|  | 	} finally { | ||||||
|  | 		unlock(); | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type visibility = 'public' | 'home' | 'followers' | 'specified'; | type visibility = 'public' | 'home' | 'followers' | 'specified'; | ||||||
|  |  | ||||||
|  | @ -1,13 +1,23 @@ | ||||||
| import Resolver from '../../resolver'; | import Resolver from '../../resolver'; | ||||||
| import { IRemoteUser } from '../../../../models/entities/user'; | import { IRemoteUser } from '../../../../models/entities/user'; | ||||||
| import { createNote, fetchNote } from '../../models/note'; | import { createNote, fetchNote } from '../../models/note'; | ||||||
|  | import { getApId } from '../../type'; | ||||||
|  | import { getApLock } from '../../../../misc/app-lock'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 投稿作成アクティビティを捌きます |  * 投稿作成アクティビティを捌きます | ||||||
|  */ |  */ | ||||||
| export default async function(resolver: Resolver, actor: IRemoteUser, note: any, silent = false): Promise<void> { | export default async function(resolver: Resolver, actor: IRemoteUser, note: any, silent = false): Promise<void> { | ||||||
|  | 	const uri = getApId(note); | ||||||
|  | 
 | ||||||
|  | 	const unlock = await getApLock(uri); | ||||||
|  | 
 | ||||||
|  | 	try { | ||||||
| 		const exist = await fetchNote(note); | 		const exist = await fetchNote(note); | ||||||
| 		if (exist == null) { | 		if (exist == null) { | ||||||
| 			await createNote(note); | 			await createNote(note); | ||||||
| 		} | 		} | ||||||
|  | 	} finally { | ||||||
|  | 		unlock(); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ import { Emoji } from '../../../models/entities/emoji'; | ||||||
| import { genId } from '../../../misc/gen-id'; | import { genId } from '../../../misc/gen-id'; | ||||||
| import { fetchMeta } from '../../../misc/fetch-meta'; | import { fetchMeta } from '../../../misc/fetch-meta'; | ||||||
| import { ensure } from '../../../prelude/ensure'; | import { ensure } from '../../../prelude/ensure'; | ||||||
|  | import { getApLock } from '../../../misc/app-lock'; | ||||||
| 
 | 
 | ||||||
| const logger = apLogger; | const logger = apLogger; | ||||||
| 
 | 
 | ||||||
|  | @ -257,6 +258,9 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver): | ||||||
| 	const meta = await fetchMeta(); | 	const meta = await fetchMeta(); | ||||||
| 	if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; | 	if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; | ||||||
| 
 | 
 | ||||||
|  | 	const unlock = await getApLock(uri); | ||||||
|  | 
 | ||||||
|  | 	try { | ||||||
| 		//#region このサーバーに既に登録されていたらそれを返す
 | 		//#region このサーバーに既に登録されていたらそれを返す
 | ||||||
| 		const exist = await fetchNote(uri); | 		const exist = await fetchNote(uri); | ||||||
| 
 | 
 | ||||||
|  | @ -268,19 +272,10 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver): | ||||||
| 		// リモートサーバーからフェッチしてきて登録
 | 		// リモートサーバーからフェッチしてきて登録
 | ||||||
| 		// ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが
 | 		// ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが
 | ||||||
| 		// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
 | 		// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
 | ||||||
| 	return await createNote(uri, resolver, true).catch(e => { | 		return await createNote(uri, resolver, true); | ||||||
| 		if (e.name === 'duplicated') { | 	} finally { | ||||||
| 			return fetchNote(uri).then(note => { | 		unlock(); | ||||||
| 				if (note == null) { |  | ||||||
| 					throw new Error('something happened'); |  | ||||||
| 				} else { |  | ||||||
| 					return note; |  | ||||||
| 	} | 	} | ||||||
| 			}); |  | ||||||
| 		} else { |  | ||||||
| 			throw e; |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]> { | export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]> { | ||||||
|  |  | ||||||
|  | @ -9561,6 +9561,11 @@ redis-errors@^1.0.0, redis-errors@^1.2.0: | ||||||
|   resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" |   resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" | ||||||
|   integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= |   integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= | ||||||
| 
 | 
 | ||||||
|  | redis-lock@0.1.4: | ||||||
|  |   version "0.1.4" | ||||||
|  |   resolved "https://registry.yarnpkg.com/redis-lock/-/redis-lock-0.1.4.tgz#e83590bee22b5f01cdb65bfbd88d988045356272" | ||||||
|  |   integrity sha512-7/+zu86XVQfJVx1nHTzux5reglDiyUCDwmW7TSlvVezfhH2YLc/Rc8NE0ejQG+8/0lwKzm29/u/4+ogKeLosiA== | ||||||
|  | 
 | ||||||
| redis-parser@^2.6.0: | redis-parser@^2.6.0: | ||||||
|   version "2.6.0" |   version "2.6.0" | ||||||
|   resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" |   resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue