Skip to content

Commit

Permalink
fix: device disconnection
Browse files Browse the repository at this point in the history
  • Loading branch information
oneofthezombies committed Nov 13, 2023
1 parent 4275da8 commit 0dfb76d
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 46 deletions.
2 changes: 2 additions & 0 deletions projects/console-web-server/src/module/event/event.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { SlackModule } from '../../enterprise/module/integration/slack/slack.mod
import { LicenseModule } from '../../enterprise/module/license/license.module';
import { DeviceMessageModule } from '../device-message/device-message.module';
import { LiveSessionModule } from '../live-session/live-session.module';
import { DeviceModule } from '../organization/device/device.module';
import { ProjectModule } from '../project/project.module';
import { RedisModule } from '../redis/redis.module';
import { RemoteModule } from '../remote/remote.module';
Expand Down Expand Up @@ -75,6 +76,7 @@ import { UpdateProducer } from './update-producer';
ProjectModule,
LiveSessionModule,
RedisModule,
DeviceModule,
],
providers: [
UpdateProducer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { config } from '../../config';
import { LiveSession } from '../../db/entity/live-session.entity';
import { LiveSessionService } from '../live-session/live-session.service';
import { DoguLogger } from '../logger/logger';
import { DeviceCommandService } from '../organization/device/device-command.service';
import { RedisService } from '../redis/redis.service';
import { EventConsumer } from './event.consumer';
import { EventProducer } from './event.producer';
Expand All @@ -22,32 +23,33 @@ export class LiveSessionUpdater implements OnModuleInit, OnModuleDestroy {
private readonly redis: RedisService,
private readonly logger: DoguLogger,
private readonly liveSessionService: LiveSessionService,
private readonly deviceCommandService: DeviceCommandService,
) {
this.eventProducer = new EventProducer({
redis,
logger,
key: config.redis.key.updateLiveSession,
produceInterval: 1000,
eventExpireTimeout: 60 * 1000,
onProduce: async () => {
return '0';
onProduce: async (): Promise<string> => {
return Promise.resolve('0');
},
});
this.eventConsumer = new EventConsumer({
redis,
logger,
key: config.redis.key.updateLiveSession,
consumeInterval: 1000,
onConsume: () => this.update(),
onConsume: async (): Promise<void> => this.update(),
});
}

onModuleInit() {
onModuleInit(): void {
this.eventProducer.start();
this.eventConsumer.start();
}

onModuleDestroy() {
onModuleDestroy(): void {
this.eventProducer.stop();
this.eventConsumer.stop();
}
Expand Down Expand Up @@ -166,7 +168,7 @@ export class LiveSessionUpdater implements OnModuleInit, OnModuleDestroy {
});

if (toCloses.length > 0) {
return await this.liveSessionService.closeInTransaction(manager, toCloses);
return await LiveSessionService.closeInTransaction(this.logger, this.deviceCommandService, manager, toCloses);
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CloudLicenseBase, DevicePropCamel, DeviceUsageState, LiveSessionCreateRequestBodyDto, LiveSessionFindQueryDto, OrganizationPropCamel } from '@dogu-private/console';
import { DeviceConnectionState, LiveSessionActiveStates, LiveSessionId, LiveSessionState, OrganizationId } from '@dogu-private/types';
import { errorify } from '@dogu-tech/common';
import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource, EntityManager, In } from 'typeorm';
Expand All @@ -16,6 +17,46 @@ import { RedisService } from '../redis/redis.service';

@Injectable()
export class LiveSessionService {
static updateLiveSessionToClosed(liveSession: LiveSession): LiveSession {
liveSession.state = LiveSessionState.CLOSED;
liveSession.closedAt = new Date();
return liveSession;
}

static async closeInTransaction(logger: DoguLogger, deviceCommandService: DeviceCommandService, manager: EntityManager, liveSessions: LiveSession[]): Promise<LiveSession[]> {
const toCloseds = liveSessions.map((liveSession) => LiveSessionService.updateLiveSessionToClosed(liveSession));
const closeds = await manager.getRepository(LiveSession).save(toCloseds);

logger.debug('LiveSessionService.close.liveSessions', {
liveSessions,
});

const deviceIds = liveSessions.map((liveSession) => liveSession.deviceId);
const devices = await manager.getRepository(Device).find({
where: {
deviceId: In(deviceIds),
},
});
devices.forEach((device) => {
device.usageState = DeviceUsageState.PREPARING;
});
await manager.getRepository(Device).save(devices);
logger.debug('LiveSessionService.close.devices', {
devices,
});

devices.forEach((device) => {
deviceCommandService.reset(device.organizationId, device.deviceId, device.serial).catch((error) => {
logger.error('LiveSessionService.close.reset error', {
error: errorify(error),
device,
});
});
});

return closeds;
}

constructor(
@InjectDataSource()
private readonly dataSource: DataSource,
Expand Down Expand Up @@ -128,42 +169,6 @@ export class LiveSessionService {
/**
* @description do NOT access this.dataSource in this method
*/
async closeInTransaction(manager: EntityManager, liveSessions: LiveSession[]): Promise<LiveSession[]> {
liveSessions.forEach((liveSession) => {
liveSession.state = LiveSessionState.CLOSED;
liveSession.closedAt = new Date();
});
const closeds = await manager.getRepository(LiveSession).save(liveSessions);

this.logger.debug('LiveSessionService.close.liveSessions', {
liveSessions,
});

const deviceIds = liveSessions.map((liveSession) => liveSession.deviceId);
const devices = await manager.getRepository(Device).find({
where: {
deviceId: In(deviceIds),
},
});
devices.forEach((device) => {
device.usageState = DeviceUsageState.PREPARING;
});
await manager.getRepository(Device).save(devices);
this.logger.debug('LiveSessionService.close.devices', {
devices,
});

devices.forEach((device) => {
this.deviceCommandService.reset(device.organizationId, device.deviceId, device.serial).catch((error) => {
this.logger.error('LiveSessionService.close.reset error', {
error,
device,
});
});
});

return closeds;
}

async closeByLiveSessionId(liveSessionId: LiveSessionId): Promise<LiveSession> {
const liveSession = await this.dataSource.getRepository(LiveSession).findOne({ where: { liveSessionId } });
Expand All @@ -177,7 +182,7 @@ export class LiveSessionService {
}

const closedSession = await this.dataSource.transaction(async (manager) => {
const rv = await this.closeInTransaction(manager, [liveSession]);
const rv = await LiveSessionService.closeInTransaction(this.logger, this.deviceCommandService, manager, [liveSession]);
return rv[0];
});

Expand All @@ -192,7 +197,7 @@ export class LiveSessionService {
});
await this.updateCloudLicenseId(liveSessionId, cloudLicenseId);
await this.updateCloudLicenseLiveTestingHeartbeat(cloudLicenseId);
await this.cloudLicenseService.startUpdateLiveTesting(cloudLicenseId, {
this.cloudLicenseService.startUpdateLiveTesting(cloudLicenseId, {
onOpen: async (close) => {
await this.subscribeCloseEvent(liveSessionId, () => {
close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import {
WriteDeviceRunTimeInfosRequestBody,
} from '@dogu-private/console-host-agent';
import { FindDeviceBySerialQuery, UpdateDeviceRequestBody } from '@dogu-private/console-host-agent/src/http-specs/private-device';
import { DeviceId, DEVICE_DISPLAY_ERROR_MAX_LENGTH, findDeviceModelNameByModelId, OrganizationId } from '@dogu-private/types';
import { DeviceId, DEVICE_DISPLAY_ERROR_MAX_LENGTH, findDeviceModelNameByModelId, LiveSessionState, OrganizationId } from '@dogu-private/types';
import { Instance, transformAndValidate } from '@dogu-tech/common';
import { Body, ConflictException, Controller, Get, NotFoundException, Param, Patch, Post, Query } from '@nestjs/common';
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import { DataSource, Not, Repository } from 'typeorm';
import { Device, DEVICE_DEFAULT_MAX_PARALLEL_JOBS_IF_IS_HOST } from '../../db/entity/device.entity';
import { LiveSession } from '../../db/entity/live-session.entity';
import { HOST_ACTION_TYPE } from '../auth/auth.types';
import { HostPermission } from '../auth/decorators';
import { DeviceMessageQueue } from '../device-message/device-message.queue';
import { InfluxDbDeviceService } from '../influxdb/influxdb-device.service';
import { LiveSessionService } from '../live-session/live-session.service';
import { DoguLogger } from '../logger/logger';
import { DeviceStatusService } from '../organization/device/device-status.service';
import { IsDeviceExist } from '../organization/device/device.decorators';
Expand Down Expand Up @@ -134,6 +136,9 @@ export class PrivateDeviceController {
);
await DeviceStatusService.updateDeviceBrowserInstallations(manager, deviceId, browserInstallations);
await DeviceStatusService.updateDeviceRunners(manager, deviceId);
const liveSessions = await manager.getRepository(LiveSession).find({ where: { deviceId, state: Not(LiveSessionState.CLOSED) } });
const toCloseLiveSessions = liveSessions.map((liveSession) => LiveSessionService.updateLiveSessionToClosed(liveSession));
await manager.getRepository(LiveSession).save(toCloseLiveSessions);
});
}

Expand Down

0 comments on commit 0dfb76d

Please sign in to comment.