mirror of https://github.com/nocodb/nocodb
Browse Source
* feat: clean-up job * feat: source cleanup * fix: remove unused method * feat: move jobs-redis to a static class * feat: release sources from in-memory db on update & delete * fix: skip calls if job redis not available * fix: error handling on connection delete --------- Co-authored-by: mertmit <mertmit99@gmail.com>pull/8557/head
Raju Udava
6 months ago
committed by
GitHub
9 changed files with 238 additions and 160 deletions
@ -1,91 +0,0 @@ |
|||||||
import { Injectable, Logger } from '@nestjs/common'; |
|
||||||
import Redis from 'ioredis'; |
|
||||||
import { InstanceTypes } from '~/interface/Jobs'; |
|
||||||
|
|
||||||
@Injectable() |
|
||||||
export class JobsRedisService { |
|
||||||
protected logger = new Logger(JobsRedisService.name); |
|
||||||
|
|
||||||
private redisClient: Redis; |
|
||||||
private redisSubscriber: Redis; |
|
||||||
private unsubscribeCallbacks: { [key: string]: () => void } = {}; |
|
||||||
|
|
||||||
public primaryCallbacks: { [key: string]: (...args) => void } = {}; |
|
||||||
public workerCallbacks: { [key: string]: (...args) => void } = {}; |
|
||||||
|
|
||||||
constructor() { |
|
||||||
this.redisClient = new Redis(process.env.NC_REDIS_JOB_URL); |
|
||||||
this.redisSubscriber = new Redis(process.env.NC_REDIS_JOB_URL); |
|
||||||
|
|
||||||
if (process.env.NC_WORKER_CONTAINER === 'true') { |
|
||||||
this.redisSubscriber.subscribe(InstanceTypes.WORKER); |
|
||||||
} else { |
|
||||||
this.redisSubscriber.subscribe(InstanceTypes.PRIMARY); |
|
||||||
} |
|
||||||
|
|
||||||
const onMessage = (channel, message) => { |
|
||||||
const args = message.split(':'); |
|
||||||
const command = args.shift(); |
|
||||||
if (channel === InstanceTypes.WORKER) { |
|
||||||
this.workerCallbacks[command] && this.workerCallbacks[command](...args); |
|
||||||
} else if (channel === InstanceTypes.PRIMARY) { |
|
||||||
this.primaryCallbacks[command] && |
|
||||||
this.primaryCallbacks[command](...args); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
this.redisSubscriber.on('message', onMessage); |
|
||||||
} |
|
||||||
|
|
||||||
publish(channel: string, message: string | any) { |
|
||||||
if (typeof message === 'string') { |
|
||||||
this.redisClient.publish(channel, message); |
|
||||||
} else { |
|
||||||
try { |
|
||||||
this.redisClient.publish(channel, JSON.stringify(message)); |
|
||||||
} catch (e) { |
|
||||||
this.logger.error(e); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
subscribe(channel: string, callback: (message: any) => void) { |
|
||||||
this.redisSubscriber.subscribe(channel); |
|
||||||
|
|
||||||
const onMessage = (_channel, message) => { |
|
||||||
try { |
|
||||||
message = JSON.parse(message); |
|
||||||
} catch (e) {} |
|
||||||
callback(message); |
|
||||||
}; |
|
||||||
|
|
||||||
this.redisSubscriber.on('message', onMessage); |
|
||||||
this.unsubscribeCallbacks[channel] = () => { |
|
||||||
this.redisSubscriber.unsubscribe(channel); |
|
||||||
this.redisSubscriber.off('message', onMessage); |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
unsubscribe(channel: string) { |
|
||||||
if (this.unsubscribeCallbacks[channel]) { |
|
||||||
this.unsubscribeCallbacks[channel](); |
|
||||||
delete this.unsubscribeCallbacks[channel]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
workerCount(): Promise<number> { |
|
||||||
return new Promise((resolve, reject) => { |
|
||||||
this.redisClient.publish( |
|
||||||
InstanceTypes.WORKER, |
|
||||||
'count', |
|
||||||
(error, numberOfSubscribers) => { |
|
||||||
if (error) { |
|
||||||
reject(0); |
|
||||||
} else { |
|
||||||
resolve(numberOfSubscribers); |
|
||||||
} |
|
||||||
}, |
|
||||||
); |
|
||||||
}); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,155 @@ |
|||||||
|
import { Logger } from '@nestjs/common'; |
||||||
|
import Redis from 'ioredis'; |
||||||
|
import type { InstanceCommands } from '~/interface/Jobs'; |
||||||
|
import { InstanceTypes } from '~/interface/Jobs'; |
||||||
|
|
||||||
|
export class JobsRedis { |
||||||
|
private static initialized = false; |
||||||
|
|
||||||
|
public static available = process.env.NC_REDIS_JOB_URL ? true : false; |
||||||
|
|
||||||
|
protected static logger = new Logger(JobsRedis.name); |
||||||
|
|
||||||
|
private static redisClient: Redis; |
||||||
|
private static redisSubscriber: Redis; |
||||||
|
private static unsubscribeCallbacks: { [key: string]: () => Promise<void> } = |
||||||
|
{}; |
||||||
|
|
||||||
|
public static primaryCallbacks: { |
||||||
|
[key: string]: (...args) => Promise<void>; |
||||||
|
} = {}; |
||||||
|
public static workerCallbacks: { [key: string]: (...args) => Promise<void> } = |
||||||
|
{}; |
||||||
|
|
||||||
|
static async init() { |
||||||
|
if (this.initialized) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!JobsRedis.available) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
this.initialized = true; |
||||||
|
|
||||||
|
this.redisClient = new Redis(process.env.NC_REDIS_JOB_URL); |
||||||
|
this.redisSubscriber = new Redis(process.env.NC_REDIS_JOB_URL); |
||||||
|
|
||||||
|
if (process.env.NC_WORKER_CONTAINER === 'true') { |
||||||
|
await this.redisSubscriber.subscribe(InstanceTypes.WORKER); |
||||||
|
} else { |
||||||
|
await this.redisSubscriber.subscribe(InstanceTypes.PRIMARY); |
||||||
|
} |
||||||
|
|
||||||
|
const onMessage = async (channel, message) => { |
||||||
|
const args = message.split(':'); |
||||||
|
const command = args.shift(); |
||||||
|
if (channel === InstanceTypes.WORKER) { |
||||||
|
this.workerCallbacks[command] && |
||||||
|
(await this.workerCallbacks[command](...args)); |
||||||
|
} else if (channel === InstanceTypes.PRIMARY) { |
||||||
|
this.primaryCallbacks[command] && |
||||||
|
(await this.primaryCallbacks[command](...args)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
this.redisSubscriber.on('message', onMessage); |
||||||
|
} |
||||||
|
|
||||||
|
static async publish(channel: string, message: string | any) { |
||||||
|
if (!this.initialized) { |
||||||
|
if (!JobsRedis.available) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
await this.init(); |
||||||
|
} |
||||||
|
|
||||||
|
if (typeof message === 'string') { |
||||||
|
await this.redisClient.publish(channel, message); |
||||||
|
} else { |
||||||
|
try { |
||||||
|
await this.redisClient.publish(channel, JSON.stringify(message)); |
||||||
|
} catch (e) { |
||||||
|
this.logger.error(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static async subscribe( |
||||||
|
channel: string, |
||||||
|
callback: (message: any) => Promise<void>, |
||||||
|
) { |
||||||
|
if (!this.initialized) { |
||||||
|
if (!JobsRedis.available) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
await this.init(); |
||||||
|
} |
||||||
|
|
||||||
|
await this.redisSubscriber.subscribe(channel); |
||||||
|
|
||||||
|
const onMessage = async (_channel, message) => { |
||||||
|
try { |
||||||
|
message = JSON.parse(message); |
||||||
|
} catch (e) {} |
||||||
|
await callback(message); |
||||||
|
}; |
||||||
|
|
||||||
|
this.redisSubscriber.on('message', onMessage); |
||||||
|
this.unsubscribeCallbacks[channel] = async () => { |
||||||
|
await this.redisSubscriber.unsubscribe(channel); |
||||||
|
this.redisSubscriber.off('message', onMessage); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
static async unsubscribe(channel: string) { |
||||||
|
if (!this.initialized) { |
||||||
|
if (!JobsRedis.available) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
await this.init(); |
||||||
|
} |
||||||
|
|
||||||
|
if (this.unsubscribeCallbacks[channel]) { |
||||||
|
await this.unsubscribeCallbacks[channel](); |
||||||
|
delete this.unsubscribeCallbacks[channel]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static async workerCount(): Promise<number> { |
||||||
|
if (!this.initialized) { |
||||||
|
if (!JobsRedis.available) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
await this.init(); |
||||||
|
} |
||||||
|
|
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
this.redisClient.publish( |
||||||
|
InstanceTypes.WORKER, |
||||||
|
'count', |
||||||
|
(error, numberOfSubscribers) => { |
||||||
|
if (error) { |
||||||
|
reject(0); |
||||||
|
} else { |
||||||
|
resolve(numberOfSubscribers); |
||||||
|
} |
||||||
|
}, |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
static async emitWorkerCommand(command: InstanceCommands, ...args: any[]) { |
||||||
|
const data = `${command}${args.length ? `:${args.join(':')}` : ''}`; |
||||||
|
await JobsRedis.publish(InstanceTypes.WORKER, data); |
||||||
|
} |
||||||
|
|
||||||
|
static async emitPrimaryCommand(command: InstanceCommands, ...args: any[]) { |
||||||
|
const data = `${command}${args.length ? `:${args.join(':')}` : ''}`; |
||||||
|
await JobsRedis.publish(InstanceTypes.PRIMARY, data); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue