@adalo/metrics 0.0.0-staging.1
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.
- package/.env.example +14 -0
- package/.eslintignore +3 -0
- package/.eslintrc +61 -0
- package/.github/pull_request_template.md +14 -0
- package/.github/workflows/code-style.yml +29 -0
- package/.github/workflows/deploy-staging.yml +34 -0
- package/.github/workflows/deploy.yml +29 -0
- package/.github/workflows/tests.yml +17 -0
- package/.idea/codeStyles/Project.xml +101 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/git_toolbox_prj.xml +15 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/prettier.xml +6 -0
- package/.idea/vcs.xml +6 -0
- package/.prettierrc +10 -0
- package/README-health.md +234 -0
- package/README.md +120 -0
- package/__tests__/metricsRedisClient.test.js +138 -0
- package/babel.config.js +20 -0
- package/lib/health/databaseChecker.d.ts +43 -0
- package/lib/health/databaseChecker.d.ts.map +1 -0
- package/lib/health/databaseChecker.js +189 -0
- package/lib/health/databaseChecker.js.map +1 -0
- package/lib/health/healthCheckCache.d.ts +59 -0
- package/lib/health/healthCheckCache.d.ts.map +1 -0
- package/lib/health/healthCheckCache.js +187 -0
- package/lib/health/healthCheckCache.js.map +1 -0
- package/lib/health/healthCheckClient.d.ts +124 -0
- package/lib/health/healthCheckClient.d.ts.map +1 -0
- package/lib/health/healthCheckClient.js +324 -0
- package/lib/health/healthCheckClient.js.map +1 -0
- package/lib/health/healthCheckUtils.d.ts +52 -0
- package/lib/health/healthCheckUtils.d.ts.map +1 -0
- package/lib/health/healthCheckUtils.js +129 -0
- package/lib/health/healthCheckUtils.js.map +1 -0
- package/lib/health/healthCheckWorker.d.ts +2 -0
- package/lib/health/healthCheckWorker.d.ts.map +1 -0
- package/lib/health/healthCheckWorker.js +70 -0
- package/lib/health/healthCheckWorker.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +105 -0
- package/lib/index.js.map +1 -0
- package/lib/metrics/baseMetricsClient.d.ts +174 -0
- package/lib/metrics/baseMetricsClient.d.ts.map +1 -0
- package/lib/metrics/baseMetricsClient.js +428 -0
- package/lib/metrics/baseMetricsClient.js.map +1 -0
- package/lib/metrics/metricsClient.d.ts +95 -0
- package/lib/metrics/metricsClient.d.ts.map +1 -0
- package/lib/metrics/metricsClient.js +239 -0
- package/lib/metrics/metricsClient.js.map +1 -0
- package/lib/metrics/metricsDatabaseClient.d.ts +74 -0
- package/lib/metrics/metricsDatabaseClient.d.ts.map +1 -0
- package/lib/metrics/metricsDatabaseClient.js +218 -0
- package/lib/metrics/metricsDatabaseClient.js.map +1 -0
- package/lib/metrics/metricsQueueRedisClient.d.ts +57 -0
- package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -0
- package/lib/metrics/metricsQueueRedisClient.js +277 -0
- package/lib/metrics/metricsQueueRedisClient.js.map +1 -0
- package/lib/metrics/metricsRedisClient.d.ts +71 -0
- package/lib/metrics/metricsRedisClient.d.ts.map +1 -0
- package/lib/metrics/metricsRedisClient.js +370 -0
- package/lib/metrics/metricsRedisClient.js.map +1 -0
- package/lib/redisUtils.d.ts +53 -0
- package/lib/redisUtils.d.ts.map +1 -0
- package/lib/redisUtils.js +140 -0
- package/lib/redisUtils.js.map +1 -0
- package/package.json +66 -0
- package/scripts/README.md +43 -0
- package/scripts/clearMetrics.js +6 -0
- package/src/health/databaseChecker.js +183 -0
- package/src/health/healthCheckCache.js +216 -0
- package/src/health/healthCheckClient.js +347 -0
- package/src/health/healthCheckUtils.js +125 -0
- package/src/health/healthCheckWorker.js +71 -0
- package/src/index.ts +9 -0
- package/src/metrics/baseMetricsClient.js +494 -0
- package/src/metrics/metricsClient.js +284 -0
- package/src/metrics/metricsDatabaseClient.js +236 -0
- package/src/metrics/metricsQueueRedisClient.js +352 -0
- package/src/metrics/metricsRedisClient.js +417 -0
- package/src/redisUtils.js +155 -0
- package/tsconfig.json +19 -0
- package/tsconfig.types.json +11 -0
package/.env.example
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
BUILD_APP_NAME="staging"
|
|
2
|
+
HOSTNAME="staging-queue-metricus"
|
|
3
|
+
BUILD_DYNO_PROCESS_TYPE="web"
|
|
4
|
+
METRICS_ENABLED=true
|
|
5
|
+
# Set to true to collect app_requests_total and app_requests_total_duration (default: false)
|
|
6
|
+
# METRICS_HTTP_ENABLED=false
|
|
7
|
+
METRICS_LOG_VALUES=true
|
|
8
|
+
METRICS_PUSHGATEWAY_URL=https://vm-agent.infradalogs.adalo.com
|
|
9
|
+
METRICS_PUSHGATEWAY_SECRET="METRICS_PUSHGATEWAY_SECRET"
|
|
10
|
+
METRICS_INTERVAL_SEC=60
|
|
11
|
+
# METRICS_REMOVE_OLD_METRICS=false (set true to delete this instance's metrics from VM on exit)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
package/.eslintignore
ADDED
package/.eslintrc
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"airbnb-base",
|
|
4
|
+
"airbnb-typescript/base",
|
|
5
|
+
"prettier" // Prettier must always be last as it overrides all other configs (https://github.com/prettier/eslint-config-prettier)
|
|
6
|
+
],
|
|
7
|
+
"parser": "@typescript-eslint/parser",
|
|
8
|
+
"parserOptions": {
|
|
9
|
+
"project": "./tsconfig.json"
|
|
10
|
+
},
|
|
11
|
+
"plugins": ["@typescript-eslint"],
|
|
12
|
+
"env": {
|
|
13
|
+
"browser": true,
|
|
14
|
+
"commonjs": true,
|
|
15
|
+
"es6": true,
|
|
16
|
+
"node": true
|
|
17
|
+
},
|
|
18
|
+
"settings": {
|
|
19
|
+
"import/resolver": {
|
|
20
|
+
"typescript": {} // this loads <rootdir>/tsconfig.json
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"overrides": [
|
|
24
|
+
{
|
|
25
|
+
"files": ["**/*.test.js"],
|
|
26
|
+
"env": {
|
|
27
|
+
"jest": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"rules": {
|
|
32
|
+
"curly": ["error", "multi-line"],
|
|
33
|
+
"lines-between-class-members": [
|
|
34
|
+
"error",
|
|
35
|
+
"always",
|
|
36
|
+
{
|
|
37
|
+
"exceptAfterSingleLine": true
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"@typescript-eslint/no-unused-vars": [
|
|
41
|
+
"error",
|
|
42
|
+
{
|
|
43
|
+
"vars": "all",
|
|
44
|
+
"args": "none",
|
|
45
|
+
"ignoreRestSiblings": true
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
|
|
49
|
+
// Disabled rules
|
|
50
|
+
"arrow-body-style": "off",
|
|
51
|
+
"import/prefer-default-export": "off",
|
|
52
|
+
"no-param-reassign": "off",
|
|
53
|
+
"no-restricted-syntax": "off",
|
|
54
|
+
"@typescript-eslint/no-shadow": "off",
|
|
55
|
+
"no-underscore-dangle": "off",
|
|
56
|
+
"class-methods-use-this": "off",
|
|
57
|
+
"no-await-in-loop": "off",
|
|
58
|
+
"no-console": "off",
|
|
59
|
+
"no-continue": "off"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Problem
|
|
2
|
+
*Describe the specific bug or product issue that's being fixed.*
|
|
3
|
+
|
|
4
|
+
## Solution
|
|
5
|
+
*What did you do to fix it? Remember gif's or video of frontend fixes.*
|
|
6
|
+
|
|
7
|
+
## Additional Notes (optional)
|
|
8
|
+
|
|
9
|
+
- Link to Tech Spec in Notion
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Related PR's (optional)
|
|
13
|
+
|
|
14
|
+
## New Packages, Environment Variables (optional)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Check Prettier and ESLint conformance
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
prettier:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check out repo
|
|
15
|
+
uses: actions/checkout@v3
|
|
16
|
+
|
|
17
|
+
- name: Setup Node.js
|
|
18
|
+
uses: actions/setup-node@v3
|
|
19
|
+
with:
|
|
20
|
+
node-version: '18.x'
|
|
21
|
+
|
|
22
|
+
- name: Installing dependencies
|
|
23
|
+
run: yarn install --frozen-lockfile
|
|
24
|
+
|
|
25
|
+
- name: Run Prettier checks
|
|
26
|
+
run: yarn prettier:check
|
|
27
|
+
|
|
28
|
+
- name: Run ESLint checks
|
|
29
|
+
run: yarn lint:check
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Publish a staging build to npm with tag "staging".
|
|
2
|
+
# Push (or merge) to branch "staging" to publish. Consuming apps can then use
|
|
3
|
+
# "@adalo/metrics": "staging"
|
|
4
|
+
# to test changes on staging before merging to main.
|
|
5
|
+
name: Deploy Staging
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches:
|
|
10
|
+
- staging
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-and-publish:
|
|
14
|
+
if: "!contains(github.event.head_commit.message, 'skip ci')"
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v3
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
- name: Setup Node.js
|
|
21
|
+
uses: actions/setup-node@v3
|
|
22
|
+
with:
|
|
23
|
+
node-version: '18.x'
|
|
24
|
+
registry-url: 'https://registry.npmjs.org'
|
|
25
|
+
- name: Installing dependencies
|
|
26
|
+
run: yarn install --frozen-lockfile
|
|
27
|
+
- name: Build lib
|
|
28
|
+
run: yarn run build
|
|
29
|
+
- name: Set staging version
|
|
30
|
+
run: npm version 0.0.0-staging.${{ github.run_number }} --no-git-tag-version --allow-same-version
|
|
31
|
+
- name: Publish to NPM (tag staging)
|
|
32
|
+
run: npm publish --tag staging
|
|
33
|
+
env:
|
|
34
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Deploy Production
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
if: "!contains(github.event.head_commit.message, 'skip ci')"
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v3
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0 # Fetch all commits across all branches. Default behaviour is efficient but breaks merge-release.
|
|
16
|
+
- name: Setup Node.js
|
|
17
|
+
uses: actions/setup-node@v3
|
|
18
|
+
with:
|
|
19
|
+
node-version: '18.x'
|
|
20
|
+
registry-url: 'https://registry.npmjs.org'
|
|
21
|
+
- name: Installing dependencies
|
|
22
|
+
run: yarn install --frozen-lockfile
|
|
23
|
+
- name: Build lib
|
|
24
|
+
run: yarn run build
|
|
25
|
+
- name: Publish to NPM
|
|
26
|
+
uses: AdaloHQ/npm-publish@v1
|
|
27
|
+
env:
|
|
28
|
+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
29
|
+
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: pull_request
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v3
|
|
10
|
+
- name: Setup Node.js
|
|
11
|
+
uses: actions/setup-node@v3
|
|
12
|
+
with:
|
|
13
|
+
node-version: '18.x'
|
|
14
|
+
- name: Installing dependencies
|
|
15
|
+
run: yarn install --frozen-lockfile
|
|
16
|
+
- name: Running tests
|
|
17
|
+
run: yarn test
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<component name="ProjectCodeStyleConfiguration">
|
|
2
|
+
<code_scheme name="Project" version="173">
|
|
3
|
+
<option name="OTHER_INDENT_OPTIONS">
|
|
4
|
+
<value>
|
|
5
|
+
<option name="INDENT_SIZE" value="2" />
|
|
6
|
+
<option name="TAB_SIZE" value="2" />
|
|
7
|
+
</value>
|
|
8
|
+
</option>
|
|
9
|
+
<HTMLCodeStyleSettings>
|
|
10
|
+
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
|
11
|
+
</HTMLCodeStyleSettings>
|
|
12
|
+
<JSCodeStyleSettings version="0">
|
|
13
|
+
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
|
14
|
+
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
|
15
|
+
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACKETS" value="true" />
|
|
16
|
+
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
|
17
|
+
<option name="USE_DOUBLE_QUOTES" value="false" />
|
|
18
|
+
<option name="FORCE_QUOTE_STYlE" value="true" />
|
|
19
|
+
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
|
20
|
+
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
|
21
|
+
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
|
22
|
+
<option name="SPACES_WITHIN_INTERPOLATION_EXPRESSIONS" value="true" />
|
|
23
|
+
</JSCodeStyleSettings>
|
|
24
|
+
<TypeScriptCodeStyleSettings version="0">
|
|
25
|
+
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
|
26
|
+
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
|
27
|
+
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
|
28
|
+
<option name="USE_DOUBLE_QUOTES" value="false" />
|
|
29
|
+
<option name="FORCE_QUOTE_STYlE" value="true" />
|
|
30
|
+
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
|
31
|
+
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
|
32
|
+
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
|
33
|
+
</TypeScriptCodeStyleSettings>
|
|
34
|
+
<VueCodeStyleSettings>
|
|
35
|
+
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
|
36
|
+
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
|
37
|
+
</VueCodeStyleSettings>
|
|
38
|
+
<codeStyleSettings language="CSS">
|
|
39
|
+
<indentOptions>
|
|
40
|
+
<option name="INDENT_SIZE" value="2" />
|
|
41
|
+
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
42
|
+
<option name="TAB_SIZE" value="2" />
|
|
43
|
+
</indentOptions>
|
|
44
|
+
</codeStyleSettings>
|
|
45
|
+
<codeStyleSettings language="Gherkin">
|
|
46
|
+
<indentOptions>
|
|
47
|
+
<option name="TAB_SIZE" value="2" />
|
|
48
|
+
</indentOptions>
|
|
49
|
+
</codeStyleSettings>
|
|
50
|
+
<codeStyleSettings language="HTML">
|
|
51
|
+
<option name="SOFT_MARGINS" value="80" />
|
|
52
|
+
<indentOptions>
|
|
53
|
+
<option name="INDENT_SIZE" value="2" />
|
|
54
|
+
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
55
|
+
<option name="TAB_SIZE" value="2" />
|
|
56
|
+
</indentOptions>
|
|
57
|
+
</codeStyleSettings>
|
|
58
|
+
<codeStyleSettings language="JSON">
|
|
59
|
+
<indentOptions>
|
|
60
|
+
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
61
|
+
</indentOptions>
|
|
62
|
+
</codeStyleSettings>
|
|
63
|
+
<codeStyleSettings language="JavaScript">
|
|
64
|
+
<option name="BLOCK_COMMENT_ADD_SPACE" value="true" />
|
|
65
|
+
<option name="IF_BRACE_FORCE" value="1" />
|
|
66
|
+
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
|
67
|
+
<option name="WHILE_BRACE_FORCE" value="1" />
|
|
68
|
+
<option name="FOR_BRACE_FORCE" value="1" />
|
|
69
|
+
<option name="SOFT_MARGINS" value="80" />
|
|
70
|
+
<indentOptions>
|
|
71
|
+
<option name="INDENT_SIZE" value="2" />
|
|
72
|
+
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
73
|
+
<option name="TAB_SIZE" value="2" />
|
|
74
|
+
</indentOptions>
|
|
75
|
+
</codeStyleSettings>
|
|
76
|
+
<codeStyleSettings language="LESS">
|
|
77
|
+
<indentOptions>
|
|
78
|
+
<option name="TAB_SIZE" value="2" />
|
|
79
|
+
</indentOptions>
|
|
80
|
+
</codeStyleSettings>
|
|
81
|
+
<codeStyleSettings language="TypeScript">
|
|
82
|
+
<option name="BLOCK_COMMENT_ADD_SPACE" value="true" />
|
|
83
|
+
<option name="IF_BRACE_FORCE" value="1" />
|
|
84
|
+
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
|
85
|
+
<option name="WHILE_BRACE_FORCE" value="1" />
|
|
86
|
+
<option name="FOR_BRACE_FORCE" value="1" />
|
|
87
|
+
<option name="SOFT_MARGINS" value="80" />
|
|
88
|
+
<indentOptions>
|
|
89
|
+
<option name="INDENT_SIZE" value="2" />
|
|
90
|
+
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
91
|
+
<option name="TAB_SIZE" value="2" />
|
|
92
|
+
</indentOptions>
|
|
93
|
+
</codeStyleSettings>
|
|
94
|
+
<codeStyleSettings language="Vue">
|
|
95
|
+
<option name="SOFT_MARGINS" value="80" />
|
|
96
|
+
<indentOptions>
|
|
97
|
+
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
98
|
+
</indentOptions>
|
|
99
|
+
</codeStyleSettings>
|
|
100
|
+
</code_scheme>
|
|
101
|
+
</component>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="GitToolBoxProjectSettings">
|
|
4
|
+
<option name="commitMessageIssueKeyValidationOverride">
|
|
5
|
+
<BoolValueOverride>
|
|
6
|
+
<option name="enabled" value="true" />
|
|
7
|
+
</BoolValueOverride>
|
|
8
|
+
</option>
|
|
9
|
+
<option name="commitMessageValidationEnabledOverride">
|
|
10
|
+
<BoolValueOverride>
|
|
11
|
+
<option name="enabled" value="true" />
|
|
12
|
+
</BoolValueOverride>
|
|
13
|
+
</option>
|
|
14
|
+
</component>
|
|
15
|
+
</project>
|
package/.idea/vcs.xml
ADDED
package/.prettierrc
ADDED
package/README-health.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Health Check Client
|
|
2
|
+
|
|
3
|
+
Provides health check endpoints for external monitoring services like BetterStack.
|
|
4
|
+
Uses a background worker to periodically check database connections and cache results in Redis,
|
|
5
|
+
preventing HTTP requests from triggering database queries.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
1. **Worker Process**: Runs as a separate process (defined in Procfile)
|
|
10
|
+
- Performs health checks every minute (configurable)
|
|
11
|
+
- Checks database connections (and optionally Redis)
|
|
12
|
+
- Stores results in Redis cache with 60-second TTL
|
|
13
|
+
- Creates its own Redis connection with name `{appName}:healthcheck-worker`
|
|
14
|
+
|
|
15
|
+
2. **Web Processes**: Handle HTTP requests
|
|
16
|
+
- Read health check results from Redis cache only
|
|
17
|
+
- Never trigger database queries
|
|
18
|
+
- Return cached data or error if Redis unavailable
|
|
19
|
+
- Use existing system Redis client
|
|
20
|
+
|
|
21
|
+
## Environment Variables
|
|
22
|
+
|
|
23
|
+
| Variable | Description | Default |
|
|
24
|
+
|----------|-------------|---------|
|
|
25
|
+
| `BUILD_APP_NAME` | Application name for logging and Redis client naming | `unknown-app` |
|
|
26
|
+
| `HOSTNAME` | Dyno/instance ID for logging | `unknown-dyno` |
|
|
27
|
+
| `BUILD_DYNO_PROCESS_TYPE` | Process type for logging | `health-check-worker` |
|
|
28
|
+
| `HEALTH_LOG_VALUES` | Enable logging of health check refresh status (`true`/`false`) | `false` |
|
|
29
|
+
| `HEALTH_DB_MAX_CONNECT_LATENCY_MS` | Fail DB health if connection acquisition/connect time exceeds this (ms) | `1000` |
|
|
30
|
+
| `REDIS_URL` | Redis connection URL for cache | - |
|
|
31
|
+
| `DATABASE_URL` | Main PostgreSQL connection URL (for backend worker) | - |
|
|
32
|
+
| `META_DB_URL` | Main PostgreSQL connection URL (for database worker) | - |
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Backend Service
|
|
37
|
+
|
|
38
|
+
**Endpoint Client** (`lib/healthCheck.ts`):
|
|
39
|
+
```typescript
|
|
40
|
+
import { createHealthCheckEndpointClient } from '@adalo/metrics'
|
|
41
|
+
import { redisClient } from './redis'
|
|
42
|
+
|
|
43
|
+
export const healthCheckClient = createHealthCheckEndpointClient({
|
|
44
|
+
redisClient,
|
|
45
|
+
includeRedisCheck: true,
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Worker** (`health-check-worker.ts`):
|
|
50
|
+
```typescript
|
|
51
|
+
import { createHealthCheckWorker } from '@adalo/metrics'
|
|
52
|
+
import { createNamedRedisClient } from './lib/redis'
|
|
53
|
+
|
|
54
|
+
const healthCheckRedisClient = createNamedRedisClient('healthcheck-worker')
|
|
55
|
+
|
|
56
|
+
const runHealthCheckWorker = createHealthCheckWorker({
|
|
57
|
+
databaseUrl: process.env.DATABASE_URL,
|
|
58
|
+
databaseName: 'backend',
|
|
59
|
+
redisClient: healthCheckRedisClient,
|
|
60
|
+
includeRedisCheck: true,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
runHealthCheckWorker().catch(err => {
|
|
64
|
+
console.error('[HealthCheckWorker] Fatal error:', err)
|
|
65
|
+
process.exit(1)
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Procfile**:
|
|
70
|
+
```
|
|
71
|
+
health-check-worker: node dist/health-check-worker.js
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Database Service
|
|
75
|
+
|
|
76
|
+
**Endpoint Client** (`lib/healthCheck.ts`):
|
|
77
|
+
```typescript
|
|
78
|
+
import { createHealthCheckEndpointClient } from '@adalo/metrics'
|
|
79
|
+
import { redisClient } from './cache/client'
|
|
80
|
+
|
|
81
|
+
export const healthCheckClient = createHealthCheckEndpointClient({
|
|
82
|
+
redisClient,
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Worker** (`workers/health-check-worker.ts`):
|
|
87
|
+
```typescript
|
|
88
|
+
import { createHealthCheckWorker } from '@adalo/metrics'
|
|
89
|
+
import { getClusterDatabaseUrls } from '../utils/clusterUrls'
|
|
90
|
+
import { createNamedRedisClient } from '../lib/cache/client'
|
|
91
|
+
|
|
92
|
+
const healthCheckRedisClient = createNamedRedisClient('healthcheck-worker')
|
|
93
|
+
|
|
94
|
+
const runHealthCheckWorker = createHealthCheckWorker({
|
|
95
|
+
databaseUrl: process.env.META_DB_URL,
|
|
96
|
+
databaseName: 'meta_db',
|
|
97
|
+
additionalDatabaseUrls: getClusterDatabaseUrls(),
|
|
98
|
+
redisClient: healthCheckRedisClient,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
runHealthCheckWorker().catch(err => {
|
|
102
|
+
console.error('[HealthCheckWorker] Fatal error:', err)
|
|
103
|
+
process.exit(1)
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Procfile**:
|
|
108
|
+
```
|
|
109
|
+
health-check-worker: node --enable-source-maps dist/workers/health-check-worker.js
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Endpoints
|
|
113
|
+
|
|
114
|
+
### Default Endpoint
|
|
115
|
+
|
|
116
|
+
By default, `registerHealthEndpoint()` registers at `/health`:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
healthCheckClient.registerHealthEndpoint(app)
|
|
120
|
+
// Registers: GET /health
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Custom Endpoints
|
|
124
|
+
|
|
125
|
+
You can also register custom endpoints that use the same cached data:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Backend: /healthcheck (old endpoint, still works)
|
|
129
|
+
app.get('/healthcheck', healthCheckClient.healthHandler())
|
|
130
|
+
|
|
131
|
+
// Database: /hc (old endpoint, still works)
|
|
132
|
+
app.get('/hc', healthCheckClient.healthHandler())
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Response Format
|
|
136
|
+
|
|
137
|
+
### Healthy Response
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"status": "ok",
|
|
141
|
+
"lastCheckAt": 1738143477926,
|
|
142
|
+
"resources": {
|
|
143
|
+
"DATABASE_URL": { "status": "ok", "connectMs": 9.2 },
|
|
144
|
+
"REDIS_URL": { "status": "ok" }
|
|
145
|
+
},
|
|
146
|
+
"isStale": false,
|
|
147
|
+
"config": { "checkIntervalMs": 30000, "staleThresholdMs": 180000, "checkTimeoutMs": 15000 }
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Unhealthy Response (Redis Cache Unavailable)
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"status": "unhealthy",
|
|
155
|
+
"timestamp": "2026-01-23T08:27:57.926Z",
|
|
156
|
+
"checks": {
|
|
157
|
+
"redis": {
|
|
158
|
+
"status": "unhealthy",
|
|
159
|
+
"error": "Redis cache read failed: connection refused"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"errors": ["Redis cache read failed: connection refused"]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Unhealthy Response (Database Issue)
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"status": "error",
|
|
170
|
+
"lastCheckAt": 1738143477926,
|
|
171
|
+
"resources": {
|
|
172
|
+
"DATABASE_URL": { "status": "error", "error": "connection timeout", "connectMs": 5000 }
|
|
173
|
+
},
|
|
174
|
+
"isStale": false,
|
|
175
|
+
"config": { "checkIntervalMs": 30000, "staleThresholdMs": 180000, "checkTimeoutMs": 15000 }
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Status Codes
|
|
180
|
+
|
|
181
|
+
- `200` - healthy or degraded
|
|
182
|
+
- `503` - unhealthy (at least one component failed)
|
|
183
|
+
|
|
184
|
+
## Configuration Options
|
|
185
|
+
|
|
186
|
+
### Worker Options
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
createHealthCheckWorker({
|
|
190
|
+
databaseUrl: string, // Required: Main database URL
|
|
191
|
+
databaseName: string, // Required: Database name
|
|
192
|
+
redisClient: any, // Required: Redis client instance (created with createNamedRedisClient('healthcheck-worker'))
|
|
193
|
+
additionalDatabaseUrls?: object, // Optional: Additional clusters (database service)
|
|
194
|
+
includeRedisCheck?: boolean, // Optional: Include Redis in health check (default: false)
|
|
195
|
+
refreshIntervalMs?: number, // Optional: Refresh interval (default: 30000ms)
|
|
196
|
+
appName?: string, // Optional: App name (defaults to BUILD_APP_NAME)
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Endpoint Client Options
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
createHealthCheckEndpointClient({
|
|
204
|
+
redisClient: any, // Required: Existing Redis client from system
|
|
205
|
+
includeRedisCheck?: boolean, // Optional: Include Redis in response (default: false)
|
|
206
|
+
})
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Logging Format
|
|
210
|
+
|
|
211
|
+
Worker logs follow the same format as metrics:
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
[health-check-worker] [app-name] [dyno-id] [HealthCheck] Starting health check worker...
|
|
215
|
+
[health-check-worker] [app-name] [dyno-id] [HealthCheck] Refresh interval: 60000ms
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
When `HEALTH_LOG_VALUES=true`, additional logs are printed:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
[health-check-worker] [app-name] [dyno-id] [HealthCheck] Initial health check completed
|
|
222
|
+
[health-check-worker] [app-name] [dyno-id] [HealthCheck] Health check refreshed at 2026-01-23T08:27:57.926Z, status: healthy
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Error logs are always printed regardless of `HEALTH_LOG_VALUES` setting.
|
|
226
|
+
|
|
227
|
+
## Benefits
|
|
228
|
+
|
|
229
|
+
- **No Concurrent DB Queries**: Only worker queries database, endpoints read from cache
|
|
230
|
+
- **Same Data Per Minute**: All requests get the same cached result within the TTL period
|
|
231
|
+
- **Redis Optional**: Works even if Redis is disabled (falls back to in-memory cache per process)
|
|
232
|
+
- **Graceful Degradation**: Returns proper error format if Redis unavailable
|
|
233
|
+
- **Multi-Process Support**: Shared cache across all web process instances via Redis
|
|
234
|
+
|