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:
lborv
2025-10-09 19:54:08 +03:00
parent c3189bb2df
commit 174dbbcdba
9 changed files with 97 additions and 12 deletions

View File

@ -27,11 +27,9 @@ export class DatabaseNodeService extends DatabaseEncryptionService {
return null;
}
return nodes.reduce((optimalNode, currentNode) => {
const currentCount = currentNode.databases?.length || 0;
const optimalCount = optimalNode.databases?.length || 0;
return currentCount < optimalCount ? currentNode : optimalNode;
});
nodes.sort((a, b) => a.databases.length - b.databases.length);
return nodes[0];
}
async initDatabase(

View File

@ -4,12 +4,14 @@ import {
Column,
Entity,
JoinColumn,
ManyToMany,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { Database } from "../../databaseManager/entities/database.entity";
import { FunctionEntity } from "../../query/entities/function.entity";
import { RedisNode } from "../../redisManager/entities/redis.node.entity";
@Entity("project")
export class Project {
@ -31,4 +33,8 @@ export class Project {
@OneToMany(() => FunctionEntity, (functionEntity) => functionEntity.project)
functions: FunctionEntity[];
@ManyToMany(() => RedisNode, (redisNode) => redisNode.projects)
@JoinColumn()
redisNodes: RedisNode[];
}

View File

@ -24,4 +24,10 @@ export class ProjectService {
database: { id: databaseId },
});
}
updateRedisNode(projectId: string, redisNodeId: { id: string }[]) {
return this.projectRepository.update(projectId, {
redisNodes: redisNodeId,
});
}
}

View File

@ -1,3 +1,4 @@
import { RedisNodeService } from "./../../redisManager/redisNode/redis.node.service";
import { Inject, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Query } from "../entities/query.entity";
@ -25,6 +26,7 @@ export class QueryExecuterService {
@Inject(FunctionService)
private readonly functionService: FunctionService,
readonly databaseManagerService: DatabaseManagerService,
readonly redisNodeService: RedisNodeService,
@InjectQueue(QUEUE_NAMES.QUERY) private queryQueue: Queue
) {
this.queueEvents = new QueueEvents(this.queryQueue.name);

View File

@ -12,6 +12,7 @@ import { QueueModule } from "src/queue/queue.module";
import { FunctionEntity } from "./entities/function.entity";
import { FunctionService } from "src/project/function/function.service";
import { FunctionController } from "src/project/function/function.controller";
import { RedisManagerModule } from "src/redisManager/redisManager.module";
@Module({
imports: [
@ -19,6 +20,7 @@ import { FunctionController } from "src/project/function/function.controller";
forwardRef(() => DatabaseManagerModule),
forwardRef(() => ApiModule),
forwardRef(() => QueueModule),
forwardRef(() => RedisManagerModule),
TypeOrmModule.forFeature([Query, FunctionEntity]),
],
controllers: [QueryController, CommandController, FunctionController],

View File

@ -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")
export class RedisNode {
@ -16,4 +23,8 @@ export class RedisNode {
@Column({ type: "varchar", length: 255 })
password: string | null;
@ManyToMany(() => Project, (project) => project.redisNodes)
@JoinColumn()
projects: Project[];
}

View File

@ -4,9 +4,14 @@ import { RedisNode } from "./entities/redis.node.entity";
import { RedisManagerController } from "./redis.manager.controller";
import { RedisNodeService } from "./redisNode/redis.node.service";
import { ApiModule } from "src/api/api.module";
import { ProjectModule } from "src/project/project.module";
@Module({
imports: [forwardRef(() => ApiModule), TypeOrmModule.forFeature([RedisNode])],
imports: [
forwardRef(() => ApiModule),
forwardRef(() => ProjectModule),
TypeOrmModule.forFeature([RedisNode]),
],
controllers: [RedisManagerController],
providers: [RedisNodeService],
exports: [RedisNodeService],

View File

@ -1,13 +1,16 @@
import { Injectable } from "@nestjs/common";
import { Inject, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { RedisNode } from "../entities/redis.node.entity";
import { Repository } from "typeorm";
import { ProjectService } from "src/project/project.service";
@Injectable()
export class RedisNodeService {
constructor(
@InjectRepository(RedisNode)
private readonly redisNodeRepository: Repository<RedisNode>
private readonly redisNodeRepository: Repository<RedisNode>,
@Inject(ProjectService)
private readonly projectService: ProjectService
) {}
async create(
@ -36,15 +39,55 @@ export class RedisNodeService {
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;
port: number;
username: 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) {
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 {

View File

@ -3,6 +3,7 @@ import { DatabasePlugin } from "./plugins/database.plugin";
import { Query } from "src/query/entities/query.entity";
import { QueryPlugin } from "./plugins/query.plugin";
import { AxiosPlugin } from "./plugins/axios.plugin";
import { RedisPlugin } from "./plugins/redis.plugin";
export const registeredPlugins = {
db: async (service: QueryExecuterService, query: Query) => {
@ -18,6 +19,17 @@ export const registeredPlugins = {
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 () => {
return AxiosPlugin.init("axios");
},