feat: integrate RedisNode management into Project and Query services; enhance RedisNodeService with optimal node selection and connection options; update vm.constants to include RedisPlugin
This commit is contained in:
@ -27,11 +27,9 @@ export class DatabaseNodeService extends DatabaseEncryptionService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes.reduce((optimalNode, currentNode) => {
|
nodes.sort((a, b) => a.databases.length - b.databases.length);
|
||||||
const currentCount = currentNode.databases?.length || 0;
|
|
||||||
const optimalCount = optimalNode.databases?.length || 0;
|
return nodes[0];
|
||||||
return currentCount < optimalCount ? currentNode : optimalNode;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async initDatabase(
|
async initDatabase(
|
||||||
|
|||||||
@ -4,12 +4,14 @@ import {
|
|||||||
Column,
|
Column,
|
||||||
Entity,
|
Entity,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
|
ManyToMany,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
OneToOne,
|
OneToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
} from "typeorm";
|
} from "typeorm";
|
||||||
import { Database } from "../../databaseManager/entities/database.entity";
|
import { Database } from "../../databaseManager/entities/database.entity";
|
||||||
import { FunctionEntity } from "../../query/entities/function.entity";
|
import { FunctionEntity } from "../../query/entities/function.entity";
|
||||||
|
import { RedisNode } from "../../redisManager/entities/redis.node.entity";
|
||||||
|
|
||||||
@Entity("project")
|
@Entity("project")
|
||||||
export class Project {
|
export class Project {
|
||||||
@ -31,4 +33,8 @@ export class Project {
|
|||||||
|
|
||||||
@OneToMany(() => FunctionEntity, (functionEntity) => functionEntity.project)
|
@OneToMany(() => FunctionEntity, (functionEntity) => functionEntity.project)
|
||||||
functions: FunctionEntity[];
|
functions: FunctionEntity[];
|
||||||
|
|
||||||
|
@ManyToMany(() => RedisNode, (redisNode) => redisNode.projects)
|
||||||
|
@JoinColumn()
|
||||||
|
redisNodes: RedisNode[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,4 +24,10 @@ export class ProjectService {
|
|||||||
database: { id: databaseId },
|
database: { id: databaseId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateRedisNode(projectId: string, redisNodeId: { id: string }[]) {
|
||||||
|
return this.projectRepository.update(projectId, {
|
||||||
|
redisNodes: redisNodeId,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { RedisNodeService } from "./../../redisManager/redisNode/redis.node.service";
|
||||||
import { Inject, Injectable } from "@nestjs/common";
|
import { Inject, Injectable } from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { Query } from "../entities/query.entity";
|
import { Query } from "../entities/query.entity";
|
||||||
@ -25,6 +26,7 @@ export class QueryExecuterService {
|
|||||||
@Inject(FunctionService)
|
@Inject(FunctionService)
|
||||||
private readonly functionService: FunctionService,
|
private readonly functionService: FunctionService,
|
||||||
readonly databaseManagerService: DatabaseManagerService,
|
readonly databaseManagerService: DatabaseManagerService,
|
||||||
|
readonly redisNodeService: RedisNodeService,
|
||||||
@InjectQueue(QUEUE_NAMES.QUERY) private queryQueue: Queue
|
@InjectQueue(QUEUE_NAMES.QUERY) private queryQueue: Queue
|
||||||
) {
|
) {
|
||||||
this.queueEvents = new QueueEvents(this.queryQueue.name);
|
this.queueEvents = new QueueEvents(this.queryQueue.name);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { QueueModule } from "src/queue/queue.module";
|
|||||||
import { FunctionEntity } from "./entities/function.entity";
|
import { FunctionEntity } from "./entities/function.entity";
|
||||||
import { FunctionService } from "src/project/function/function.service";
|
import { FunctionService } from "src/project/function/function.service";
|
||||||
import { FunctionController } from "src/project/function/function.controller";
|
import { FunctionController } from "src/project/function/function.controller";
|
||||||
|
import { RedisManagerModule } from "src/redisManager/redisManager.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -19,6 +20,7 @@ import { FunctionController } from "src/project/function/function.controller";
|
|||||||
forwardRef(() => DatabaseManagerModule),
|
forwardRef(() => DatabaseManagerModule),
|
||||||
forwardRef(() => ApiModule),
|
forwardRef(() => ApiModule),
|
||||||
forwardRef(() => QueueModule),
|
forwardRef(() => QueueModule),
|
||||||
|
forwardRef(() => RedisManagerModule),
|
||||||
TypeOrmModule.forFeature([Query, FunctionEntity]),
|
TypeOrmModule.forFeature([Query, FunctionEntity]),
|
||||||
],
|
],
|
||||||
controllers: [QueryController, CommandController, FunctionController],
|
controllers: [QueryController, CommandController, FunctionController],
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
import { Project } from "../../project/entities/project.entity";
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToMany,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from "typeorm";
|
||||||
|
|
||||||
@Entity("redisNode")
|
@Entity("redisNode")
|
||||||
export class RedisNode {
|
export class RedisNode {
|
||||||
@ -16,4 +23,8 @@ export class RedisNode {
|
|||||||
|
|
||||||
@Column({ type: "varchar", length: 255 })
|
@Column({ type: "varchar", length: 255 })
|
||||||
password: string | null;
|
password: string | null;
|
||||||
|
|
||||||
|
@ManyToMany(() => Project, (project) => project.redisNodes)
|
||||||
|
@JoinColumn()
|
||||||
|
projects: Project[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,14 @@ import { RedisNode } from "./entities/redis.node.entity";
|
|||||||
import { RedisManagerController } from "./redis.manager.controller";
|
import { RedisManagerController } from "./redis.manager.controller";
|
||||||
import { RedisNodeService } from "./redisNode/redis.node.service";
|
import { RedisNodeService } from "./redisNode/redis.node.service";
|
||||||
import { ApiModule } from "src/api/api.module";
|
import { ApiModule } from "src/api/api.module";
|
||||||
|
import { ProjectModule } from "src/project/project.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [forwardRef(() => ApiModule), TypeOrmModule.forFeature([RedisNode])],
|
imports: [
|
||||||
|
forwardRef(() => ApiModule),
|
||||||
|
forwardRef(() => ProjectModule),
|
||||||
|
TypeOrmModule.forFeature([RedisNode]),
|
||||||
|
],
|
||||||
controllers: [RedisManagerController],
|
controllers: [RedisManagerController],
|
||||||
providers: [RedisNodeService],
|
providers: [RedisNodeService],
|
||||||
exports: [RedisNodeService],
|
exports: [RedisNodeService],
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Inject, Injectable } from "@nestjs/common";
|
||||||
import { InjectRepository } from "@nestjs/typeorm";
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
import { RedisNode } from "../entities/redis.node.entity";
|
import { RedisNode } from "../entities/redis.node.entity";
|
||||||
import { Repository } from "typeorm";
|
import { Repository } from "typeorm";
|
||||||
|
import { ProjectService } from "src/project/project.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RedisNodeService {
|
export class RedisNodeService {
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(RedisNode)
|
@InjectRepository(RedisNode)
|
||||||
private readonly redisNodeRepository: Repository<RedisNode>
|
private readonly redisNodeRepository: Repository<RedisNode>,
|
||||||
|
@Inject(ProjectService)
|
||||||
|
private readonly projectService: ProjectService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
@ -36,15 +39,55 @@ export class RedisNodeService {
|
|||||||
return this.redisNodeRepository.save(redisNode);
|
return this.redisNodeRepository.save(redisNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getConnectionOptions(id: string): Promise<{
|
async findOptimalNode(): Promise<RedisNode | null> {
|
||||||
|
const nodes = await this.redisNodeRepository.find({
|
||||||
|
relations: ["projects"],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (nodes.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.sort((a, b) => a.projects.length - b.projects.length);
|
||||||
|
|
||||||
|
return nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConnectionOptions(projectId: string): Promise<{
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}> {
|
}> {
|
||||||
const node = await this.redisNodeRepository.findOne({ where: { id } });
|
const project = await this.projectService.findById(projectId);
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
throw new Error("Project not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = project.redisNodes[0];
|
||||||
if (!node) {
|
if (!node) {
|
||||||
throw new Error("Redis node not found");
|
const newNode = await this.findOptimalNode();
|
||||||
|
|
||||||
|
if (!newNode) {
|
||||||
|
throw new Error("No Redis nodes available");
|
||||||
|
}
|
||||||
|
|
||||||
|
project.redisNodes.push(newNode);
|
||||||
|
newNode.projects.push(project);
|
||||||
|
await this.redisNodeRepository.save(newNode);
|
||||||
|
|
||||||
|
await this.projectService.updateRedisNode(
|
||||||
|
project.id,
|
||||||
|
project.redisNodes.map((n) => ({ id: n.id }))
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
host: newNode.host,
|
||||||
|
port: newNode.port,
|
||||||
|
username: newNode.user,
|
||||||
|
password: newNode.password,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { DatabasePlugin } from "./plugins/database.plugin";
|
|||||||
import { Query } from "src/query/entities/query.entity";
|
import { Query } from "src/query/entities/query.entity";
|
||||||
import { QueryPlugin } from "./plugins/query.plugin";
|
import { QueryPlugin } from "./plugins/query.plugin";
|
||||||
import { AxiosPlugin } from "./plugins/axios.plugin";
|
import { AxiosPlugin } from "./plugins/axios.plugin";
|
||||||
|
import { RedisPlugin } from "./plugins/redis.plugin";
|
||||||
|
|
||||||
export const registeredPlugins = {
|
export const registeredPlugins = {
|
||||||
db: async (service: QueryExecuterService, query: Query) => {
|
db: async (service: QueryExecuterService, query: Query) => {
|
||||||
@ -18,6 +19,17 @@ export const registeredPlugins = {
|
|||||||
|
|
||||||
return DatabasePlugin.init("db", databaseConnection);
|
return DatabasePlugin.init("db", databaseConnection);
|
||||||
},
|
},
|
||||||
|
redis: async (service: QueryExecuterService, query: Query) => {
|
||||||
|
const redisConnection = await service.redisNodeService.getConnectionOptions(
|
||||||
|
query.project.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!redisConnection) {
|
||||||
|
throw new Error("Redis connection not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedisPlugin.init("redis", redisConnection, query.project.id);
|
||||||
|
},
|
||||||
axios: async () => {
|
axios: async () => {
|
||||||
return AxiosPlugin.init("axios");
|
return AxiosPlugin.init("axios");
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user