Add support for jobs & queueables, migrations
- Create migration directives & migrators - Modify Cache classes to support array manipulation - Create Redis unit and RedisCache implementation - Create Queueable base class and Queue class that uses Cache backend
This commit is contained in:
42
src/support/cache/MemoryCache.ts
vendored
42
src/support/cache/MemoryCache.ts
vendored
@@ -8,7 +8,10 @@ export class MemoryCache extends Cache {
|
||||
/** Static collection of in-memory cache items. */
|
||||
private static cacheItems: Collection<{key: string, value: string, expires?: Date}> = new Collection<{key: string; value: string, expires?: Date}>()
|
||||
|
||||
public fetch(key: string): Awaitable<string|undefined> {
|
||||
/** Static collection of in-memory arrays. */
|
||||
private static cacheArrays: Collection<{key: string, values: string[]}> = new Collection<{key: string; values: string[]}>()
|
||||
|
||||
public fetch(key: string): string|undefined {
|
||||
const now = new Date()
|
||||
return MemoryCache.cacheItems
|
||||
.where('key', '=', key)
|
||||
@@ -41,4 +44,41 @@ export class MemoryCache extends Cache {
|
||||
public drop(key: string): Awaitable<void> {
|
||||
MemoryCache.cacheItems = MemoryCache.cacheItems.where('key', '!=', key)
|
||||
}
|
||||
|
||||
public decrement(key: string, amount = 1): Awaitable<number | undefined> {
|
||||
const nextValue = (parseInt(this.fetch(key) ?? '0', 10) ?? 0) - amount
|
||||
this.put(key, String(nextValue))
|
||||
return nextValue
|
||||
}
|
||||
|
||||
public increment(key: string, amount = 1): Awaitable<number | undefined> {
|
||||
const nextValue = (parseInt(this.fetch(key) ?? '0', 10) ?? 0) + amount
|
||||
this.put(key, String(nextValue))
|
||||
return nextValue
|
||||
}
|
||||
|
||||
public pop(key: string): Awaitable<string | undefined> {
|
||||
const value = this.fetch(key)
|
||||
this.drop(key)
|
||||
return value
|
||||
}
|
||||
|
||||
public arrayPop(key: string): Awaitable<string | undefined> {
|
||||
const arr = MemoryCache.cacheArrays.firstWhere('key', '=', key)
|
||||
if ( arr ) {
|
||||
return arr.values.shift()
|
||||
}
|
||||
}
|
||||
|
||||
public arrayPush(key: string, value: string): Awaitable<void> {
|
||||
const arr = MemoryCache.cacheArrays.firstWhere('key', '=', key)
|
||||
if ( arr ) {
|
||||
arr.values.push(value)
|
||||
} else {
|
||||
MemoryCache.cacheArrays.push({
|
||||
key,
|
||||
values: [value],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
85
src/support/cache/RedisCache.ts
vendored
Normal file
85
src/support/cache/RedisCache.ts
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
import {Cache, Maybe} from '../../util'
|
||||
import {Inject, Injectable} from '../../di'
|
||||
import {Redis} from '../redis/Redis'
|
||||
|
||||
/**
|
||||
* Redis-driven Cache implementation.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RedisCache extends Cache {
|
||||
/** The Redis service. */
|
||||
@Inject()
|
||||
protected readonly redis!: Redis
|
||||
|
||||
async arrayPop(key: string): Promise<string | undefined> {
|
||||
return this.redis.pipe()
|
||||
.tap(redis => redis.lpop(key))
|
||||
.resolve()
|
||||
}
|
||||
|
||||
async arrayPush(key: string, value: string): Promise<void> {
|
||||
await this.redis.pipe()
|
||||
.tap(redis => redis.rpush(key, value))
|
||||
.resolve()
|
||||
}
|
||||
|
||||
async decrement(key: string, amount?: number): Promise<number | undefined> {
|
||||
return this.redis.pipe()
|
||||
.tap(redis => redis.decrby(key, amount ?? 1))
|
||||
.resolve()
|
||||
}
|
||||
|
||||
async increment(key: string, amount?: number): Promise<number | undefined> {
|
||||
return this.redis.pipe()
|
||||
.tap(redis => redis.incrby(key, amount ?? 1))
|
||||
.resolve()
|
||||
}
|
||||
|
||||
async drop(key: string): Promise<void> {
|
||||
await this.redis.pipe()
|
||||
.tap(redis => redis.del(key))
|
||||
.resolve()
|
||||
}
|
||||
|
||||
async fetch(key: string): Promise<string | undefined> {
|
||||
return this.redis.pipe()
|
||||
.tap(redis => redis.get(key))
|
||||
.tap(value => value ?? undefined)
|
||||
.resolve()
|
||||
}
|
||||
|
||||
async has(key: string): Promise<boolean> {
|
||||
return this.redis.pipe()
|
||||
.tap(redis => redis.exists(key))
|
||||
.tap(numExisting => numExisting > 0)
|
||||
.resolve()
|
||||
}
|
||||
|
||||
pop(key: string): Promise<Maybe<string>> {
|
||||
return new Promise<Maybe<string>>((res, rej) => {
|
||||
this.redis.pipe()
|
||||
.tap(redis => {
|
||||
redis.multi()
|
||||
.get(key, (err, value) => {
|
||||
if ( err ) {
|
||||
rej(err)
|
||||
} else {
|
||||
res(value)
|
||||
}
|
||||
})
|
||||
.del(key)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async put(key: string, value: string, expires?: Date): Promise<void> {
|
||||
await this.redis.multi()
|
||||
.tap(redis => redis.set(key, value))
|
||||
.when(Boolean(expires), redis => {
|
||||
const seconds = Math.round(((new Date()).getTime() - expires!.getTime()) / 1000) // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
return redis.expire(key, seconds)
|
||||
})
|
||||
.tap(pipeline => pipeline.exec())
|
||||
.resolve()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user