aws-powertools / powertools-lambda-typescript

Powertools is a developer toolkit to implement Serverless best practices and increase developer velocity.
https://docs.powertools.aws.dev/lambda/typescript/latest/
MIT No Attribution
1.53k stars 134 forks source link

Feature request: Support LLRT (Low Latency Runtime) #2050

Open NimmLor opened 6 months ago

NimmLor commented 6 months ago

Use case

This feature request is about the beta of the new lightweight JavaScript runtime LLRT (Low Latency Runtime).

Currently, the Logger crashes when importing the "console" module.

Demo repository: https://github.com/NimmLor/aws-cdk-llrt-demo

{
  "errorType": "ReferenceError",
  "errorMessage": "Error resolving module '/var/task/console' from '/var/task/index.js'",
  "stackTrace": [
    "    at ../../node_modules/@aws-lambda-powertools/logger/lib/Logger.js (/var/task/index.js:1652:26)",
    "    at __require2 (/var/task/index.js:15:17)",
    "    at ../../node_modules/@aws-lambda-powertools/logger/lib/index.js (/var/task/index.js:2555:18)",
    "    at __require2 (/var/task/index.js:15:17)",
    "    at <anonymous> (/var/task/index.js:2562:29)",
    ""
  ]
}
// esbuild output, failing code snippet

/** ... **/
var require_Logger = __commonJS({
  "../../node_modules/@aws-lambda-powertools/logger/lib/Logger.js"(exports) {
    "use strict";
    var __importDefault = exports && exports.__importDefault || function(mod) {
      return mod && mod.__esModule ? mod : { "default": mod };
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.Logger = void 0;
    var node_crypto_1 = __require("crypto");
    var node_console_1 = __require("console");
/** ... **/
// index.ts

import { Logger } from '@aws-lambda-powertools/logger'

export const handler = async () => {
  const logger = new Logger()
  logger.info('Hello world')

  return {
    body: JSON.stringify({
      hello: 'world',
    }),
    statusCode: 200,
  }
}

Solution/User Experience

LLRT offers up to over 10x faster startup and up to 2x overall lower cost compared to other JavaScript runtimes running on AWS Lambda

It would be great if the aws-lambda-powertools would support this runtime, usage should be exactly the same as for nodejs runtimes.

Alternative solutions

No response

Acknowledgment

Future readers

Please react with 👍 and your use case to help us understand customer demand.

boring-cyborg[bot] commented 6 months ago

Thanks for opening your first issue here! We'll come back to you as soon as we can. In the meantime, check out the #typescript channel on our Powertools for AWS Lambda Discord: Invite link

am29d commented 6 months ago

Hey @NimmLor ,

thanks for raising the issue. This is strange, folling the compatibility matrix of LLRT, both crypto and node are supported. Can you provide more information on the Lambda settings and how you packaged the code (versions, package.json, any relevant tools used).

Happy to look into the issue and bring Powertools to LLRT.

NimmLor commented 6 months ago

Hi @am29d,

I've quickly setup a demo repository for testing the LLRT with AWS CDK.

https://github.com/NimmLor/aws-cdk-llrt-demo

am29d commented 6 months ago

I tried the example and something is odd with the LLRT, @dreamorosi was faster than me to open an issue in their repo.

dreamorosi commented 6 months ago

Hi, I am able to reproduce the error you showed in the initial post but the issue is with LLRT rather than with Powertools.

As you mentioned, the code throws an error when importing the console module. To reproduce the issue I deployed a function with the following code, which as you can see doesn't have anything Powertools specific:

import { Console } from "node:console";

const con = new Console({ stdout: process.stdout, stderr: process.stderr });

export const handler = async (event: any) => {
  con.log("Hello, world!");
  return "Hello, world!";
};

Once deployed using the cdk-lambda-llrt construct, I get the same exact error:

  24-02-12T15:15:47.833Z        n/a     ERROR   ReferenceError: Error resolving module 'console' from '/var/task/index.mjs'
} stackTrace: [ '' ]or resolving module 'console' from '/var/task/index.mjs'',
INIT_REPORT Init Duration: 40.23 ms     Phase: init     Status: error   Error Type: Runtime.ExitError
  24-02-12T15:15:48.350Z        n/a     ERROR   ReferenceError: Error resolving module 'console' from '/var/task/index.mjs'
} stackTrace: [ '' ]or resolving module 'console' from '/var/task/index.mjs'',
INIT_REPORT Init Duration: 523.69 ms    Phase: invoke   Status: error   Error Type: Runtime.ExitError
START RequestId: 36e99bfc-806a-4326-963b-354ee212c545 Version: $LATEST
END RequestId: 36e99bfc-806a-4326-963b-354ee212c545
REPORT RequestId: 36e99bfc-806a-4326-963b-354ee212c545  Duration: 551.40 ms     Billed Duration: 551 ms Memory Size: 128 MB     Max Memory Used: 21 MB  Status: error      Error Type: Runtime.ExitError

After taking a look at the LLRT repo, and in light of this error, I think the compatibility matrix might be wrong. The table lists the console module as supported, however their API documentation doesn't include it anywhere.

I have opened an issue on their repo so that they can confirm whether the module is expected to work or not, and if not, to ask whether there's any chance that it will be implemented.

As a side note, I want to remark that according to both our tenets (see first point) and versioning policy (see dependency lifecycle section) our current priority is to officially support AWS Lambda managed runtimes - aka nodejs16x, nodejs18x, and nodejs20x at the time of writing.

LLRT has been met with remarkable positive feedback and shows a lot of promise, however while we are excited about it and eager to see where it goes, making significant changes to Powertools to support it would be premature since LLRT itself is considered experimental and subject to change.

With that said, we are open to work with the LLRT team to fix this specific issue, and other similar ones where LLRT is missing support for common Node.js APIs. Likewise, we are open to consider making changes on our side only as long as they don't impact compatibility, DX, and performance with the main Node.js runtimes.

NimmLor commented 6 months ago

Hi, thanks for the effort to reproduce the issue. I totally agree with your thoughts on the compatibility matrix and the priority of supporting AWS Lambda managed runtimes.

I kind of expected that the issue lies within LLRT rather than Powertools. I looked through the esbuild output and it seems there are no other unsupported node apis being accessed, except a single import { format } from 'node:util in the awsLogLevelShortCircuit method.

I've also looked into the powertools tracer, and it obviously is far from being compatible with LLRT. Again looking at the esbuild output, it seems that the tracer is using many of the (some planned to be supported) unsupported node apis (such as util, assert, async_hooks, dgram, fs, http, os, querystring, url). This seems to require a huge effort to get the tracer running with LLRT and I totally understand that this shouldn't be addressed until LLRT is considered stable.

I'm looking forward to the response from the LLRT team and I'm happy to help with testing and debugging if needed.

Thanks!

perpil commented 6 months ago

Not sure if this helps, but at least for the Metrics package, if I patch it to lazy-load node:console and set the POWERTOOLS_DEV env environment variable to true, metrics appear to work.

I removed the const node_console_1 = require("node:console"); from the top of the compiled ts file at: node_modules/@aws-lambda-powertools/metrics/lib/Metrics.js and updated the setConsole method to:

    setConsole() {
        if (!this.getEnvVarsService().isDevMode()) {
            const node_console_1 = require("node:console");
            this.console = new node_console_1.Console({
                stdout: process.stdout,
                stderr: process.stderr,
            });
        }
        else {
            this.console = console;
        }
    }
dreamorosi commented 6 months ago

Hi @perpil thank you for chiming in. Indeed making the import lazy and bypassing the logic that uses it via the POWERTOOLS_DEV variable helps remove the issue. Thank you for confirming that this would be the only blocker for Logger to work on LLRT.

While it's a solution, this is not the kind of DX we are aiming for, so I would still consider the lack of console a blocker and push for it to be included in the runtime.

To this end, I'd appreciate your 👍 or comment on the linked issue if you'd like LLRT to improve compatibility with Powertools, that'd help to give more weight to the request.

dreamorosi commented 5 months ago

According to awslabs/llrt#130 partial support for Console module has been added to LLRT. This would allow Logger & Metrics to work with the runtime without any patching.

As soon as they make a release and I can test the change I'll do so and report back here.

dreamorosi commented 5 months ago

LLRT have released a new beta version and the issue with Console went away, however we now have another missing API.

I have opened an issue on their repo to track this: awslabs/llrt#302

LLRT also have opened an issue to track support for Powertools in general, which you can find here: awslabs/llrt#251

NimmLor commented 5 months ago

Hi, as mentioned above, node:util is only used once in the entire codebase and can easily be replaced by a template literal to allow us to use the logger for now.

dreamorosi commented 5 months ago

Thanks for opening the PR, I left some more comments there on why I was reticent to remove it but on second thought I think it's safe to use template literals and remove the blocker.

I have modified/commented on the LLRT repo about the change on our side.

As far as I can tell we're still missing the randomInt API from node:crypto which was merged in awslabs/llrt#306 but not yet released.

Once LLRT releases the next beta, I'll test the Logger and see if we have everything. If anyone does it first I'd appreciate if they could report here.