-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: use redis-semaphore for lock management, refactor
- Loading branch information
Showing
29 changed files
with
799 additions
and
918 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 5 additions & 33 deletions
38
packages/core/lib/message-deduplication/messageDeduplicationSchemas.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,7 @@ | ||
import { z } from 'zod' | ||
|
||
// Private interface to provide JSDoc feature | ||
interface PublisherMessageDeduplicationMessageTypeInternal { | ||
/** How many seconds to keep the deduplication key in the store for a particular message type */ | ||
deduplicationWindowSeconds: number | ||
} | ||
|
||
export const PUBLISHER_MESSAGE_DEDUPLICATION_MESSAGE_TYPE_SCHEMA = z.object({ | ||
deduplicationWindowSeconds: z.number().int().gt(0), | ||
}) | ||
|
||
export type PublisherMessageDeduplicationMessageType = z.infer< | ||
typeof PUBLISHER_MESSAGE_DEDUPLICATION_MESSAGE_TYPE_SCHEMA | ||
> & | ||
PublisherMessageDeduplicationMessageTypeInternal | ||
|
||
// Private interface to provide JSDoc feature | ||
interface ConsumerMessageDeduplicationMessageTypeInternal { | ||
/** How many seconds to keep the deduplication key in the store for a particular message type after message is successfully processed */ | ||
deduplicationWindowSeconds: number | ||
|
||
/** How many seconds it is expected to take to process a message of a particular type */ | ||
maximumProcessingTimeSeconds: number | ||
} | ||
|
||
export const CONSUMER_MESSAGE_DEDUPLICATION_MESSAGE_TYPE_SCHEMA = | ||
PUBLISHER_MESSAGE_DEDUPLICATION_MESSAGE_TYPE_SCHEMA.extend({ | ||
maximumProcessingTimeSeconds: z.number().int().gt(0), | ||
}) | ||
|
||
export type ConsumerMessageDeduplicationMessageType = z.infer< | ||
typeof CONSUMER_MESSAGE_DEDUPLICATION_MESSAGE_TYPE_SCHEMA | ||
> & | ||
ConsumerMessageDeduplicationMessageTypeInternal | ||
export const MESSAGE_DEDUPLICATION_WINDOW_SECONDS_SCHEMA = z | ||
.number() | ||
.int() | ||
.gt(0) | ||
.describe('message deduplication window in seconds') |
65 changes: 28 additions & 37 deletions
65
packages/core/lib/message-deduplication/messageDeduplicationTypes.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,48 @@ | ||
import type { | ||
ConsumerMessageDeduplicationMessageType, | ||
PublisherMessageDeduplicationMessageType, | ||
} from './messageDeduplicationSchemas' | ||
import type { Either } from '@lokalise/node-core' | ||
|
||
export interface PublisherMessageDeduplicationStore { | ||
export interface ReleasableLock { | ||
release(): Promise<void> | ||
} | ||
|
||
export class AcquireLockTimeoutError extends Error {} | ||
|
||
export interface MessageDeduplicationStore { | ||
/** | ||
* Stores a deduplication key in case it does not already exist. | ||
* @param {string} key - deduplication key | ||
* @param {string} value - value to store | ||
* @param {number} ttlSeconds - time to live in seconds | ||
* @returns {boolean} - true if the key was stored, false if it already existed | ||
* @returns {Promise<boolean>} - true if the key was stored, false if it already existed | ||
*/ | ||
setIfNotExists(key: string, value: string, ttlSeconds: number): Promise<boolean> | ||
|
||
/** Retrieves value associated with deduplication key */ | ||
getByKey(key: string): Promise<string | null> | ||
} | ||
|
||
export type PublisherMessageDeduplicationMessageTypeConfig = | ||
PublisherMessageDeduplicationMessageType | ||
|
||
export interface ConsumerMessageDeduplicationStore extends PublisherMessageDeduplicationStore { | ||
/** | ||
* Retrieves TTL of the deduplication key | ||
* | ||
* Acquires locks for a given key | ||
* @param {string} key - deduplication key | ||
* @returns {number|null} - TTL of the deduplication key in seconds or null if the key does not exist | ||
* @returns {Promise<Either<AcquireLockTimeoutError | Error, ReleasableLock>>} - a promise that resolves to a ReleasableLock if the lock was acquired, AcquireLockTimeoutError error if the lock could not be acquired due to timeout, or an Error if the lock could not be acquired for another reason | ||
*/ | ||
getKeyTtl(key: string): Promise<number | null> | ||
|
||
/** Sets a value for the deduplication key or updates it if it already exists */ | ||
setOrUpdate(key: string, value: string, ttlSeconds: number): Promise<void> | ||
acquireLock(key: string): Promise<Either<AcquireLockTimeoutError | Error, ReleasableLock>> | ||
|
||
/** Deletes the deduplication key */ | ||
deleteKey(key: string): Promise<void> | ||
/** | ||
* Checks if a deduplication key exists in the store | ||
* @param {string} key - deduplication key | ||
* @returns {Promise<boolean>} - true if the key exists, false otherwise | ||
*/ | ||
keyExists(key: string): Promise<boolean> | ||
} | ||
|
||
export type ConsumerMessageDeduplicationMessageTypeConfig = ConsumerMessageDeduplicationMessageType | ||
|
||
export type MessageDeduplicationConfig< | ||
TStore extends ConsumerMessageDeduplicationStore | PublisherMessageDeduplicationStore, | ||
TConfig extends | ||
| ConsumerMessageDeduplicationMessageTypeConfig | ||
| PublisherMessageDeduplicationMessageTypeConfig, | ||
> = { | ||
export type MessageDeduplicationConfig = { | ||
/** The store to use for storage and retrieval of deduplication keys */ | ||
deduplicationStore: TStore | ||
deduplicationStore: MessageDeduplicationStore | ||
} | ||
|
||
/** The configuration for deduplication for each message type */ | ||
messageTypeToConfigMap: Record<string, TConfig> | ||
export enum DeduplicationRequester { | ||
Consumer = 'consumer', | ||
Publisher = 'publisher', | ||
} | ||
|
||
export enum ConsumerMessageDeduplicationKeyStatus { | ||
PROCESSING = 'PROCESSING', | ||
PROCESSED = 'PROCESSED', | ||
export const DEFAULT_DEDUPLICATION_WINDOW_SECONDS = 10 | ||
|
||
export const noopReleasableLock: ReleasableLock = { | ||
release: async () => {}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.