@friggframework/devtools 2.0.0-next.80 → 2.0.0-next.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,6 +21,36 @@ const AuroraResourceResolver = require('./aurora-resolver');
21
21
  const { createEmptyDiscoveryResult } = require('../shared/types/discovery-result');
22
22
  const { ResourceOwnership } = require('../shared/types/resource-ownership');
23
23
 
24
+ // Pool + timeout query params appended to DATABASE_URL for Lambda-to-Aurora
25
+ // connections. Chosen to make worker Lambdas fail loud and fast on any DB
26
+ // contention rather than silently hanging for Lambda's 900s timeout.
27
+ //
28
+ // connection_limit=2 — two pg connections per Lambda container. One is too
29
+ // tight: several core use cases (get-process.executeMany,
30
+ // field-encryption-service batches) issue in-handler
31
+ // Promise.all against Prisma, and would serialize
32
+ // behind a single slot. Two removes that cliff while
33
+ // still being safe against max_connections (at 4 ACU
34
+ // Aurora pg 15 allows ~400 connections; 200 concurrent
35
+ // Lambdas × 2 = 400, leaves cluster room for maint).
36
+ // pool_timeout=20 — wait up to 20s for a pool slot, then throw P2024.
37
+ // Still fail-fast relative to 900s Lambda cap; gives
38
+ // in-handler fan-outs headroom.
39
+ // connect_timeout=10 — bound TCP/TLS handshake.
40
+ // socket_timeout=60 — kill dead client sockets (server never responds).
41
+ // options=-c statement_timeout=30000 -c lock_timeout=10000
42
+ // — Postgres-side hard caps. A query stuck >30s aborts
43
+ // with SQLSTATE 57014; a lock wait >10s aborts with
44
+ // SQLSTATE 55P03. URL encoding per libpq URI rules
45
+ // (space→%20, `=`→%3D inside the options value).
46
+ const LAMBDA_DATABASE_URL_QUERY_PARAMS = [
47
+ 'connection_limit=2',
48
+ 'pool_timeout=20',
49
+ 'connect_timeout=10',
50
+ 'socket_timeout=60',
51
+ 'options=-c%20statement_timeout%3D30000%20-c%20lock_timeout%3D10000',
52
+ ].join('&');
53
+
24
54
  class AuroraBuilder extends InfrastructureBuilder {
25
55
  constructor() {
26
56
  super();
@@ -415,9 +445,16 @@ class AuroraBuilder extends InfrastructureBuilder {
415
445
  ],
416
446
  // Note: PubliclyAccessible is NOT supported on Aurora clusters
417
447
  // It should only be set on DB instances (see FriggAuroraInstance below)
448
+ // MaxCapacity default bumped 1 → 4 ACU: at 0.5–1 ACU Aurora is
449
+ // CPU-starved under 20-way concurrent writes from a Lambda
450
+ // fan-out sync, which starves worker queries and compounds
451
+ // the tail-latency problem. 4 ACU is still cheap (scales to
452
+ // min when idle) and gives the DB enough headroom to
453
+ // absorb bursty sync traffic. Apps can still override both
454
+ // via app definition dbConfig.
418
455
  ServerlessV2ScalingConfiguration: {
419
456
  MinCapacity: dbConfig.minCapacity || 0.5,
420
- MaxCapacity: dbConfig.maxCapacity || 1,
457
+ MaxCapacity: dbConfig.maxCapacity || 4,
421
458
  },
422
459
  EnableHttpEndpoint: false,
423
460
  BackupRetentionPeriod: 7,
@@ -494,6 +531,10 @@ class AuroraBuilder extends InfrastructureBuilder {
494
531
  result.environment.DATABASE_PORT = String(dbConfig.port || 5432);
495
532
  result.environment.DATABASE_NAME = dbConfig.database || 'frigg';
496
533
  result.environment.DATABASE_USER = dbConfig.username || 'postgres';
534
+ // Consumers that build DATABASE_URL from components at runtime MUST
535
+ // append `?${DATABASE_URL_PARAMS}` to get the same hang-prevention
536
+ // timeouts as the managed path.
537
+ result.environment.DATABASE_URL_PARAMS = LAMBDA_DATABASE_URL_QUERY_PARAMS;
497
538
 
498
539
  console.log(` ✅ Using existing cluster: ${dbConfig.endpoint}`);
499
540
  }
@@ -724,13 +765,18 @@ exports.handler = async (event, context) => {
724
765
  result.environment.DATABASE_HOST = discoveredResources.auroraClusterEndpoint;
725
766
  result.environment.DATABASE_PORT = String(discoveredResources.auroraPort || 5432);
726
767
  result.environment.DATABASE_NAME = dbName;
768
+ // Consumers that build DATABASE_URL from components at runtime MUST
769
+ // append `?${DATABASE_URL_PARAMS}` to get the same hang-prevention
770
+ // timeouts as the managed path.
771
+ result.environment.DATABASE_URL_PARAMS = LAMBDA_DATABASE_URL_QUERY_PARAMS;
727
772
 
728
773
  // Note: DATABASE_URL is NOT set here to avoid Serverless variable resolution errors
729
774
  // The application (Frigg Core) should construct it at runtime from:
730
- // DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD
775
+ // DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD, DATABASE_URL_PARAMS
731
776
 
732
777
  console.log(' ℹ️ No Secrets Manager secret found - set DATABASE_USER and DATABASE_PASSWORD in Lambda environment');
733
778
  console.log(' ℹ️ Application will construct DATABASE_URL at runtime from DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD');
779
+ console.log(' ℹ️ Append `?${DATABASE_URL_PARAMS}` to the constructed URL for pool/timeout safety.');
734
780
  console.log(' ℹ️ Or enable autoCreateCredentials=true to automatically create and rotate credentials');
735
781
  }
736
782
 
@@ -790,9 +836,11 @@ exports.handler = async (event, context) => {
790
836
  return `{{resolve:secretsmanager:${secretRefValue}:SecretString:password}}`;
791
837
  };
792
838
 
839
+ // Query params are defined at module scope (LAMBDA_DATABASE_URL_QUERY_PARAMS)
840
+ // so runtime-URL-construction paths can emit the same timeouts as an env var.
793
841
  return {
794
842
  'Fn::Sub': [
795
- `postgresql://\${Username}:\${Password}@\${Host}:\${Port}/\${Database}`,
843
+ `postgresql://\${Username}:\${Password}@\${Host}:\${Port}/\${Database}?${LAMBDA_DATABASE_URL_QUERY_PARAMS}`,
796
844
  {
797
845
  Username: resolveSecretRef(secretRef),
798
846
  Password: resolveSecretPassword(secretRef),
@@ -556,7 +556,17 @@ describe('AuroraBuilder', () => {
556
556
 
557
557
  // Should use Fn::Sub with nested Fn::Sub to resolve the Ref
558
558
  expect(dbUrl['Fn::Sub']).toBeDefined();
559
- expect(dbUrl['Fn::Sub'][0]).toBe('postgresql://${Username}:${Password}@${Host}:${Port}/${Database}');
559
+ // Template includes pool + timeout query params to prevent
560
+ // silent 15-min Lambda hangs on DB contention.
561
+ expect(dbUrl['Fn::Sub'][0]).toMatch(
562
+ /^postgresql:\/\/\$\{Username\}:\$\{Password\}@\$\{Host\}:\$\{Port\}\/\$\{Database\}\?/
563
+ );
564
+ expect(dbUrl['Fn::Sub'][0]).toContain('connection_limit=2');
565
+ expect(dbUrl['Fn::Sub'][0]).toContain('pool_timeout=20');
566
+ expect(dbUrl['Fn::Sub'][0]).toContain('connect_timeout=10');
567
+ expect(dbUrl['Fn::Sub'][0]).toContain('socket_timeout=60');
568
+ expect(dbUrl['Fn::Sub'][0]).toContain('statement_timeout%3D30000');
569
+ expect(dbUrl['Fn::Sub'][0]).toContain('lock_timeout%3D10000');
560
570
 
561
571
  // The Username and Password should use Fn::Sub to resolve the secret Ref, not literal "[object Object]"
562
572
  expect(dbUrl['Fn::Sub'][1].Username['Fn::Sub']).toBeDefined();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.80",
4
+ "version": "2.0.0-next.82",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -25,9 +25,9 @@
25
25
  "@babel/eslint-parser": "^7.18.9",
26
26
  "@babel/parser": "^7.25.3",
27
27
  "@babel/traverse": "^7.25.3",
28
- "@friggframework/core": "2.0.0-next.80",
29
- "@friggframework/schemas": "2.0.0-next.80",
30
- "@friggframework/test": "2.0.0-next.80",
28
+ "@friggframework/core": "2.0.0-next.82",
29
+ "@friggframework/schemas": "2.0.0-next.82",
30
+ "@friggframework/test": "2.0.0-next.82",
31
31
  "@hapi/boom": "^10.0.1",
32
32
  "@inquirer/prompts": "^5.3.8",
33
33
  "axios": "^1.7.2",
@@ -55,8 +55,8 @@
55
55
  "validate-npm-package-name": "^5.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@friggframework/eslint-config": "2.0.0-next.80",
59
- "@friggframework/prettier-config": "2.0.0-next.80",
58
+ "@friggframework/eslint-config": "2.0.0-next.82",
59
+ "@friggframework/prettier-config": "2.0.0-next.82",
60
60
  "aws-sdk-client-mock": "^4.1.0",
61
61
  "aws-sdk-client-mock-jest": "^4.1.0",
62
62
  "jest": "^30.1.3",
@@ -88,5 +88,5 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
- "gitHead": "7a66dfcb02143941411ff26aaee866afc1473df8"
91
+ "gitHead": "765f34d4ea2b2861f4a1723f8b319bfef9a5aea8"
92
92
  }