Skip to content

Commit

Permalink
feat(server): recover the application and synchronize the recovery fu…
Browse files Browse the repository at this point in the history
…nctions to sys_db (#1879)

* feat(server): recover the application and synchronize the recovery functions to sys_db

---------

Co-authored-by: HUAHUAI23 <lim@outlook.com>
  • Loading branch information
HUAHUAI23 and HUAHUAI23 committed Mar 2, 2024
1 parent 1c688b8 commit 46a0cd4
Showing 1 changed file with 111 additions and 9 deletions.
120 changes: 111 additions & 9 deletions server/src/database/database.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { GenerateAlphaNumericPassword } from 'src/utils/random'
import { MongoService } from './mongo.service'
import * as mongodb_uri from 'mongodb-uri'
import { RegionService } from 'src/region/region.service'
import { TASK_LOCK_INIT_TIME } from 'src/constants'
import {
CN_PUBLISHED_CONF,
CN_PUBLISHED_FUNCTIONS,
CN_PUBLISHED_WEBSITE_HOSTING,
TASK_LOCK_INIT_TIME,
} from 'src/constants'
import { Region } from 'src/region/entities/region'
import { SystemDatabase } from '../system-database'
import {
Expand All @@ -18,6 +23,9 @@ import { exec } from 'node:child_process'
import { promisify } from 'node:util'
import { DatabaseSyncRecord } from './entities/database-sync-record'
import { MongoClient, ObjectId } from 'mongodb'
import { DedicatedDatabaseService } from './dedicated-database/dedicated-database.service'
import { CloudFunction } from 'src/function/entities/cloud-function'
import { ApplicationConfiguration } from 'src/application/entities/application-configuration'

const p_exec = promisify(exec)

Expand All @@ -29,6 +37,7 @@ export class DatabaseService {
constructor(
private readonly mongoService: MongoService,
private readonly regionService: RegionService,
private readonly dedicatedDatabaseService: DedicatedDatabaseService,
) {}

async create(appid: string) {
Expand Down Expand Up @@ -226,11 +235,31 @@ export class DatabaseService {

async exportDatabase(appid: string, filePath: string, uid: ObjectId) {
const region = await this.regionService.findByAppId(appid)
const database = await this.findOne(appid)
assert(database, 'Database not found')
const sharedDatabase = await this.findOne(appid)
const dedicatedDatabase = await this.dedicatedDatabaseService.findOne(appid)

const connectionUri = this.getControlConnectionUri(region, database)
assert(connectionUri, 'Database connection uri not found')
if (sharedDatabase && dedicatedDatabase) {
throw new Error(
`Database ${appid} found in both shared and dedicated databases.`,
)
}

if (!sharedDatabase && !dedicatedDatabase) {
throw new Error(
`Database ${appid} not found in both shared and dedicated databases.`,
)
}
let connectionUri
if (sharedDatabase) {
connectionUri = this.getControlConnectionUri(region, sharedDatabase)
} else {
connectionUri = await this.dedicatedDatabaseService.getConnectionUri(
region,
dedicatedDatabase,
)
}

assert(connectionUri, `Database ${appid} connection uri not found`)

try {
await p_exec(
Expand All @@ -252,16 +281,39 @@ export class DatabaseService {
uid: ObjectId,
): Promise<void> {
const region = await this.regionService.findByAppId(appid)
const database = await this.findOne(appid)
assert(database, 'Database not found')

const connectionUri = this.getControlConnectionUri(region, database)
assert(connectionUri, 'Database connection uri not found')
const sharedDatabase = await this.findOne(appid)
const dedicatedDatabase = await this.dedicatedDatabaseService.findOne(appid)

if (sharedDatabase && dedicatedDatabase) {
throw new Error(
`Database ${appid} found in both shared and dedicated databases.`,
)
}

if (!sharedDatabase && !dedicatedDatabase) {
throw new Error(
`Database ${appid} not found in both shared and dedicated databases.`,
)
}
let connectionUri
if (sharedDatabase) {
connectionUri = this.getControlConnectionUri(region, sharedDatabase)
} else {
connectionUri = await this.dedicatedDatabaseService.getConnectionUri(
region,
dedicatedDatabase,
)
}
assert(connectionUri, `Database ${appid} connection uri not found`)

try {
await p_exec(
`mongorestore --uri='${connectionUri}' --gzip --archive='${filePath}' --nsFrom="${dbName}.*" --nsTo="${appid}.*" -v --nsInclude="${dbName}.*"`,
)

await this.recoverFunctionsToSystemDatabase(appid, uid)

await this.db
.collection<DatabaseSyncRecord>('DatabaseSyncRecord')
.insertOne({ uid, createdAt: new Date() })
Expand All @@ -271,4 +323,54 @@ export class DatabaseService {
throw error
}
}

async recoverFunctionsToSystemDatabase(appid: string, uid: ObjectId) {
const { db, client } =
(await this.dedicatedDatabaseService.findAndConnect(appid)) ||
(await this.findAndConnect(appid))

try {
const appFunctionCollection = db.collection(CN_PUBLISHED_FUNCTIONS)
const appConfCollection = db.collection(CN_PUBLISHED_CONF)
const appWebsiteCollection = db.collection(CN_PUBLISHED_WEBSITE_HOSTING)

const functionsExist = await this.db
.collection<CloudFunction>('CloudFunction')
.countDocuments({ appid })

if (functionsExist) {
this.logger.debug(`${appid} Functions already exist in system database`)
return
}

const funcs: CloudFunction[] = await appFunctionCollection
.find<CloudFunction>({})
.toArray()

if (funcs.length === 0) {
this.logger.debug(` ${appid} No functions for recover.`)
return
}

funcs.forEach((func) => {
delete func._id
func.appid = appid
func.createdBy = uid
})

await this.db.collection<CloudFunction>('CloudFunction').insertMany(funcs)

// sync conf
const conf = await this.db
.collection<ApplicationConfiguration>('ApplicationConfiguration')
.findOne({ appid })

await appConfCollection.deleteMany({})
await appConfCollection.insertOne(conf)

await appWebsiteCollection.deleteMany({})
} finally {
await client.close()
}
}
}

0 comments on commit 46a0cd4

Please sign in to comment.