Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): detach custom dependency, support node_module caching #1658

Merged
merged 15 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"Chakra",
"chatgpt",
"ciphertext",
"cloudbin",
"clsx",
"coll",
"compat",
Expand Down Expand Up @@ -105,6 +106,7 @@
"statefulset",
"storageclass",
"Streamable",
"stylelint",
"tailwindcss",
"tanstack",
"telepresence",
Expand All @@ -125,8 +127,7 @@
"withs",
"xmlparser",
"zcube",
"zustand",
"stylelint"
"zustand"
],
"i18n-ally.localesPaths": "web/public/locales",
"i18n-ally.enabledParsers": [
Expand Down
8 changes: 6 additions & 2 deletions runtimes/nodejs/Dockerfile.init
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
FROM lafyun/runtime-node:latest
FROM node:20

CMD [ "sh", "/app/init.sh" ]
WORKDIR /app

COPY ./init.sh /app/init.sh

CMD [ "sh", "/app/init.sh" ]
99 changes: 96 additions & 3 deletions runtimes/nodejs/init.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,108 @@
#!/bin/sh

set -e
# node ./dist/init.js

# skip init if $DEPENDENCIES is empty
if [ -z "$DEPENDENCIES" ]; then
echo "No dependencies to install."
cp -r /app/* /tmp/app
exit 0
fi

# if $NODE_MODULES_URL is not empty
if [ -n "$NODE_MODULES_PULL_URL" ]; then
echo "Downloading node_modules from $NODE_MODULES_PULL_URL"

# get start time
start_time=$(date +%s)

# temporarily disable set -e
set +e

# download node_modules.tar and untar to `node_modules`
curl -sSfL $NODE_MODULES_PULL_URL -o node_modules.tar

end_time=$(date +%s)

# if error
if [ $? -ne 0 ]; then
echo "Failed to download node_modules cache."
else
elapsed_time=$(expr $end_time - $start_time)
echo "Downloaded node_modules.tar in $elapsed_time seconds."
fi

# untar node_modules.tar
tar -xf node_modules.tar -C .

# check tar exit code
if [ $? -ne 0 ]; then
echo "Failed to extract node_modules cache."
else
end_time_2=$(date +%s)
elapsed_time_2=$(expr $end_time_2 - $end_time)
echo "Extracted node_modules cache in $elapsed_time_2 seconds."
fi

# re-enable set -e
set -e
else
echo "No node_modules cache found, continuing installation."
fi

CACHED_DEPENDENCIES=""
# if node_modules/.dependencies exists
if [ -f "node_modules/.dependencies" ]; then
CACHED_DEPENDENCIES=`cat node_modules/.dependencies`
fi

echo "Cached dependencies: $CACHED_DEPENDENCIES"
echo "Dependencies to install: $DEPENDENCIES"

# if $CACHED_DEPENDENCIES is equal to $DEPENDENCIES
if [ "$CACHED_DEPENDENCIES" = "$DEPENDENCIES" ]; then
echo "No dependencies changed since last cache build."
exit 0
else
echo "Dependencies changed since last cache build."
fi


# npm install $DEPENDENCIES
start_time=$(date +%s)
echo "npm install $DEPENDENCIES $NPM_INSTALL_FLAGS"
npm install $DEPENDENCIES $NPM_INSTALL_FLAGS
cp -r /app/* /tmp/app
end_time=$(date +%s)
elapsed_time=$(expr $end_time - $start_time)
echo "Installed dependencies in $elapsed_time seconds."

# if $NODE_MODULES_PUSH_URL is not empty
if [ -n "$NODE_MODULES_PUSH_URL" ]; then
# temporarily disable set -e
set +e

start_time=$(date +%s)
echo $DEPENDENCIES > node_modules/.dependencies
echo "Uploading node_modules to $NODE_MODULES_PUSH_URL"

# tar `node_modules` to node_modules.tar
tar -cf node_modules.tar ./node_modules

end_time_1=$(date +%s)
elapsed_time=$(expr $end_time_1 - $start_time)
echo "Compressed node_modules in $elapsed_time seconds."

# upload node_modules.tar to $NODE_MODULES_PUSH_URL
curl -sSfL -X PUT -T node_modules.tar $NODE_MODULES_PUSH_URL


if [ $? -ne 0 ]; then
echo "Failed to upload node_modules cache."
else
end_time_2=$(date +%s)
elapsed_time_2=$(expr $end_time_2 - $end_time)
echo "Uploaded node_modules.tar in $elapsed_time_2 seconds."
fi

# re-enable set -e
set -e
fi
1 change: 0 additions & 1 deletion runtimes/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"express-xml-bodyparser": "^0.3.0",
"jsonwebtoken": "^9.0.0",
"lodash": "^4.17.21",
"log4js": "^6.7.1",
"minio": "^7.0.32",
"mongodb": "^4.12.1",
"mongodb-uri": "^0.9.7",
Expand Down
4 changes: 4 additions & 0 deletions runtimes/nodejs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,8 @@ export default class Config {
static get DISABLE_MODULE_CACHE(): boolean {
return process.env.DISABLE_MODULE_CACHE === 'true'
}

static get CUSTOM_DEPENDENCY_BASE_PATH(): string {
return process.env.CUSTOM_DEPENDENCY_BASE_PATH || '/tmp/custom_dependency'
}
}
32 changes: 27 additions & 5 deletions runtimes/nodejs/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,32 @@
import { LoggerInterface, MongoAccessor } from 'database-proxy'
import Config from './config'
import * as mongodb_uri from 'mongodb-uri'
import * as log4js from 'log4js'
import { logger } from './support/logger'


// Define a noop logger for mongo accessor
class AccessorLogger implements LoggerInterface {
level: number = 0
trace(..._params: any[]): void {
}

debug(..._params: any[]): void {
}

info(..._params: any[]): void {
}

warn(..._params: any[]): void {
}

error(..._params: any[]): void {
}

fatal(..._params: any[]): void {
}

}

/**
* Database Management
*/
Expand Down Expand Up @@ -39,16 +62,15 @@ export class DatabaseAgent {
const { database } = mongodb_uri.parse(Config.DB_URI)
const accessor = new MongoAccessor(database, Config.DB_URI)

const accessorLogger: any = log4js.getLogger('accessor')
accessorLogger.level = 'warning'
accessor.setLogger(accessorLogger as LoggerInterface)
const accessorLogger = new AccessorLogger()
accessor.setLogger(accessorLogger)
accessor
.init()
.then(async () => {
logger.info('db connected')
})
.catch((error) => {
accessorLogger.error(error)
logger.error(error)
setTimeout(() => process.exit(101), 0)
})

Expand Down
20 changes: 17 additions & 3 deletions runtimes/nodejs/src/handler/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import { logger } from '../support/logger'
import { IRequest } from '../support/types'
import { parseToken } from '../support/token'
import { FunctionCache } from '../support/engine'
import Config from '../config'
import * as fs from 'fs'

const nodeModulesRoot = path.resolve(__dirname, '../../node_modules')

/**
* Gets declaration files of a dependency package
*/
export async function handlePackageTypings(req: IRequest, res: Response) {
const requestId = req['requestId']

// verify the debug token
const token = req.get('x-laf-develop-token')
Expand Down Expand Up @@ -77,9 +78,22 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
})
}

// get custom dependency types
const customDependencyPath = `${Config.CUSTOM_DEPENDENCY_BASE_PATH}/node_modules/`
if (fs.existsSync(`${customDependencyPath}/${packageName}`)) {
getThreePartyPackageTypings(req, res, customDependencyPath, packageName)
} else {
getThreePartyPackageTypings(req, res, nodeModulesRoot, packageName)
}
}



async function getThreePartyPackageTypings(req: IRequest, res: Response, basePath: string, packageName: string) {
const requestId = req['requestId']
try {
// Gets other three-party package types
const pkd = new PackageDeclaration(packageName, nodeModulesRoot)
const pkd = new PackageDeclaration(packageName, basePath)
await pkd.load()
return res.send({
code: 0,
Expand All @@ -92,4 +106,4 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
error: error.toString(),
})
}
}
}
27 changes: 23 additions & 4 deletions runtimes/nodejs/src/support/engine/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ import { FunctionCache, FunctionModuleGlobalContext } from '.'
import Config from '../../config'
import { Console } from '.'
import * as vm from 'vm'
import { createRequire } from 'node:module'

const CUSTOM_DEPENDENCY_NODE_MODULES_PATH = `${Config.CUSTOM_DEPENDENCY_BASE_PATH}/node_modules/`

export class FunctionModule {
protected static cache: Map<string, any> = new Map()

private static customRequire = createRequire(
CUSTOM_DEPENDENCY_NODE_MODULES_PATH,
)

static get(functionName: string): any {
const moduleName = `@/${functionName}`
return this.require(moduleName, [])
Expand Down Expand Up @@ -40,7 +47,13 @@ export class FunctionModule {
}
return mod
}
return require(name)

// load custom dependency from custom dependency path first
try {
return FunctionModule.customRequire(name)
} catch (e) {
return require(name)
}
}

/**
Expand All @@ -62,9 +75,11 @@ export class FunctionModule {
filename: `FunctionModule.${functionName}`,
displayErrors: true,
contextCodeGeneration: {
strings: false,
strings: true,
wasm: true,
},
} as any

const script = this.createScript(wrapped, {})
return script.runInNewContext(sandbox, options)
}
Expand Down Expand Up @@ -94,13 +109,17 @@ export class FunctionModule {
): vm.Script {
const _options = {
...options,

importModuleDynamically: async (
specifier: string,
_: vm.Script,
_importAssertions: any,
) => {
return await import(specifier)
try {
const resolvedPath = FunctionModule.customRequire.resolve(specifier)
return await import(resolvedPath)
} catch (e) {
return await import(specifier)
}
},
} as any

Expand Down
4 changes: 2 additions & 2 deletions runtimes/nodejs/start.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/sh

# source .env
echo "****** start service: node $FLAGS --experimental-fetch ./dist/index.js *******"
exec node $FLAGS ./dist/index.js
echo "****** start service: node $FLAGS --experimental-vm-modules --experimental-fetch ./dist/index.js *******"
exec node $FLAGS --experimental-vm-modules --experimental-fetch ./dist/index.js
14 changes: 6 additions & 8 deletions server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
- app storage management
- app log management
- app instance management
- app billing management
- app domain management
- app certificate management
- app metrics management

# Development

Expand All @@ -21,8 +25,6 @@
- [Telepresence](https://www.telepresence.io) for local development
- [MongoDb](https://docs.mongodb.com) basic use
- [MinIO](https://min.io) object storage
- [APISIX](https://apisix.apache.org) gateway
- [nestjs-i18n](https://nestjs-i18n.com/) i18n

## Prerequisites

Expand All @@ -44,15 +46,11 @@ telepresence connect -n laf-system
telepresence intercept laf-server -p 3000:3000 -e $(pwd)/.env

npm install
npm run watch
npm run dev
```

> Clean up

```bash
telepresence leave laf-server
```

## Troubleshooting

- `telepresence helm install` failed for `arm64 / Apple Chip` cluster, please upgrade your telepresence to `v2.11.1` or later.
```
Loading