Skip to content

Commit

Permalink
feat(server): hot load environments for runtime instead of restarting…
Browse files Browse the repository at this point in the history
… runtime (#1077)
  • Loading branch information
maslow authored Apr 25, 2023
1 parent 75ba39a commit feb70ae
Show file tree
Hide file tree
Showing 14 changed files with 1,581 additions and 2,825 deletions.
1,817 changes: 798 additions & 1,019 deletions docs/diagrams/laf-arch-full.excalidraw

Large diffs are not rendered by default.

2,481 changes: 694 additions & 1,787 deletions docs/diagrams/laf-arch-simple.excalidraw

Large diffs are not rendered by default.

Binary file removed docs/diagrams/laf-arch-simple.png
Binary file not shown.
1 change: 0 additions & 1 deletion e2e/f1-payload.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "f1",
"description": "laf e2e function(hello-laf)",
"websocket": false,
"methods": [
"GET",
"POST"
Expand Down
1 change: 0 additions & 1 deletion e2e/f2-origin-payload.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "f2",
"description": "laf e2e function(db example)",
"websocket": false,
"methods": [
"GET",
"POST"
Expand Down
2 changes: 1 addition & 1 deletion runtimes/nodejs/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
export const CLOUD_FUNCTION_COLLECTION = '__functions__'
export const POLICY_COLLECTION = '__policies__'
export const FUNCTION_LOG_COLLECTION = '__function_logs__'
export const CONFIG_COLLECTION = '__config__'
export const CONFIG_COLLECTION = '__conf__'

export const WEBSOCKET_FUNCTION_NAME = '__websocket__'
export const INTERCEPTOR_FUNCTION_NAME = '__interceptor__'
Expand Down
2 changes: 0 additions & 2 deletions runtimes/nodejs/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { MongoAccessor } from 'database-proxy'
import Config from './config'
import { createLogger, logger } from './support/logger'
import * as mongodb_uri from 'mongodb-uri'
import { FunctionCache } from './support/function-engine/cache'

/**
* Database Management
Expand Down Expand Up @@ -44,7 +43,6 @@ export class DatabaseAgent {
.init()
.then(async () => {
logger.info('db connected')
FunctionCache.initialize()
})
.catch((error) => {
logger.error(error)
Expand Down
7 changes: 7 additions & 0 deletions runtimes/nodejs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@ import xmlparser from 'express-xml-bodyparser'
// init static method of class
import './support/function-log'
import './support/cloud-sdk'
import { FunctionCache } from './support/function-engine/cache'
import { DatabaseChangeStream } from './support/db-change-stream'

const app = express()

DatabaseAgent.accessor.ready.then(() => {
FunctionCache.initialize()
DatabaseChangeStream.initialize()
})

if (process.env.NODE_ENV === 'development') {
app.use(cors())
}
Expand Down
6 changes: 5 additions & 1 deletion runtimes/nodejs/src/support/cloud-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ function createCloudSdk() {
},
sockets: WebSocketAgent.clients,
appid: Config.APP_ID,
env: process.env,
get env() {
return {
...process.env,
}
},
}

/**
Expand Down
41 changes: 41 additions & 0 deletions runtimes/nodejs/src/support/db-change-stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CONFIG_COLLECTION } from '../constants'
import { DatabaseAgent } from '../db'

import { logger } from './logger'

export class DatabaseChangeStream {
static async initialize() {
this.watchConf()
}

/**
* stream the change of cloud function
* @param
* @returns
*/
static async watchConf() {
logger.info('Listening for changes in conf collection...')
this.updateEnvironments()

const stream = DatabaseAgent.db.collection(CONFIG_COLLECTION).watch()

stream.on('change', async (_change) => {
this.updateEnvironments()
})
}

private static async updateEnvironments() {
const conf = await DatabaseAgent.db
.collection(CONFIG_COLLECTION)
.findOne({})

if (!conf) {
return
}

const environments = conf.environments || []
for (const env of environments) {
process.env[env.name] = env.value
}
}
}
4 changes: 3 additions & 1 deletion runtimes/nodejs/src/support/function-engine/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export class FunctionVm {
clearInterval: clearInterval,
setTimeout: setTimeout,
clearTimeout: clearTimeout,
process: { env: process.env },
process: {
env: { ...process.env },
},
URL: URL,
fetch: globalThis.fetch,
global: null,
Expand Down
34 changes: 23 additions & 11 deletions server/src/application/configuration.service.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
import { Injectable, Logger } from '@nestjs/common'
import { ApplicationConfiguration } from '@prisma/client'
import { CN_PUBLISHED_CONF } from 'src/constants'
import { DatabaseService } from 'src/database/database.service'
import { PrismaService } from 'src/prisma/prisma.service'

@Injectable()
export class ApplicationConfigurationService {
private readonly logger = new Logger(ApplicationConfigurationService.name)

constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly databaseService: DatabaseService,
) {}

async count(appid: string) {
return this.prisma.applicationConfiguration.count({
where: {
appid,
},
})
return this.prisma.applicationConfiguration.count({ where: { appid } })
}

async remove(appid: string) {
return this.prisma.applicationConfiguration.delete({
where: {
appid,
},
})
return this.prisma.applicationConfiguration.delete({ where: { appid } })
}

async publish(conf: ApplicationConfiguration) {
const { db, client } = await this.databaseService.findAndConnect(conf.appid)
const session = client.startSession()
try {
await session.withTransaction(async () => {
const coll = db.collection(CN_PUBLISHED_CONF)
await coll.deleteOne({ appid: conf.appid }, { session })
await coll.insertOne(conf, { session })
})
} finally {
await client.close()
}
}
}
9 changes: 8 additions & 1 deletion server/src/application/environment.service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { Injectable, Logger } from '@nestjs/common'
import { PrismaService } from 'src/prisma/prisma.service'
import { CreateEnvironmentDto } from './dto/create-env.dto'
import { ApplicationConfigurationService } from './configuration.service'

@Injectable()
export class EnvironmentVariableService {
private readonly logger = new Logger(EnvironmentVariableService.name)

constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly confService: ApplicationConfigurationService,
) {}

async updateAll(appid: string, dto: CreateEnvironmentDto[]) {
const res = await this.prisma.applicationConfiguration.update({
where: { appid },
data: { environments: { set: dto } },
})

await this.confService.publish(res)
return res.environments
}

Expand All @@ -37,6 +42,7 @@ export class EnvironmentVariableService {
data: { environments: { set: origin } },
})

await this.confService.publish(res)
return res.environments
}

Expand All @@ -54,6 +60,7 @@ export class EnvironmentVariableService {
data: { environments: { deleteMany: { where: { name } } } },
})

await this.confService.publish(res)
return res
}
}
1 change: 1 addition & 0 deletions server/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export const HTTP_METHODS = ['HEAD', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH']
export const CN_PUBLISHED_FUNCTIONS = '__functions__'
export const CN_PUBLISHED_POLICIES = '__policies__'
export const CN_FUNCTION_LOGS = '__function_logs__'
export const CN_PUBLISHED_CONF = '__conf__'

export const X_LAF_TRIGGER_TOKEN_KEY = 'x-laf-trigger-token'
export const X_LAF_DEVELOP_TOKEN_KEY = 'x-laf-develop-token'
Expand Down

0 comments on commit feb70ae

Please sign in to comment.