⚙️ ci: Bump GitHub Actions to Node.js 24 Runtimes (#14689)

* ci: bump GitHub Actions to Node.js 24 runtimes

Clear Node 20 deprecation warnings on runners by moving workflow
actions to majors that declare node24 (checkout, cache, setup-node,
artifacts, Docker buildx/build-push/qemu/login, github-script,
setup-go, Azure login/helm, create-pull-request, axe-linter).

* fix: release leader lock via ioredis on Redis Cluster

@keyv/redis EVAL can surface unhandled MOVED redirects on cluster,
so resign() logged failure and left LeadingServerUUID set. Use ioredis
for leader election SET NX / GET / Lua (same pattern as principals and
concurrency locks) so cluster redirects are retried and resignation
clears the lock.
This commit is contained in:
Marco Beretta 2026-08-07 17:25:29 +02:00 committed by GitHub
parent c570517768
commit 9e6d677751
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 258 additions and 227 deletions

View file

@ -36,8 +36,8 @@
# PLAYWRIGHT_BROWSERS_PATH: 0 # Places binaries to node_modules/@playwright/test
# TITLE_CONVO: false
# steps:
# - uses: actions/checkout@v4
# - uses: actions/setup-node@v4
# - uses: actions/checkout@v5
# - uses: actions/setup-node@v5
# with:
# node-version: 24.16.0
# cache: 'npm'
@ -64,7 +64,7 @@
# run: npm run e2e:ci
# - name: Upload playwright report
# uses: actions/upload-artifact@v3
# uses: actions/upload-artifact@v6
# if: always()
# with:
# name: playwright-report

View file

@ -24,8 +24,8 @@ jobs:
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_workflow == 'true')
steps:
- uses: actions/checkout@v4
- uses: dequelabs/axe-linter-action@v1
- uses: actions/checkout@v5
- uses: dequelabs/axe-linter-action@v2
with:
api_key: ${{ secrets.AXE_LINTER_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -28,16 +28,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -53,7 +53,7 @@ jobs:
- name: Restore data-provider build cache
id: cache-data-provider
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-provider/dist
key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -64,7 +64,7 @@ jobs:
- name: Restore data-schemas build cache
id: cache-data-schemas
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-schemas/dist
key: build-data-schemas-${{ runner.os }}-${{ hashFiles('packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -75,7 +75,7 @@ jobs:
- name: Restore api build cache
id: cache-api
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/api/dist
key: build-api-${{ runner.os }}-${{ hashFiles('packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/tsdown.config.mjs', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json') }}

View file

@ -21,16 +21,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -46,7 +46,7 @@ jobs:
- name: Restore data-provider build cache
id: cache-data-provider
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-provider/dist
key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -57,7 +57,7 @@ jobs:
- name: Restore data-schemas build cache
id: cache-data-schemas
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-schemas/dist
key: build-data-schemas-${{ runner.os }}-${{ hashFiles('packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -68,7 +68,7 @@ jobs:
- name: Restore api build cache
id: cache-api
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/api/dist
key: build-api-${{ runner.os }}-${{ hashFiles('packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/tsdown.config.mjs', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json') }}
@ -78,21 +78,21 @@ jobs:
run: npm run build:api
- name: Upload data-provider build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-data-provider
path: packages/data-provider/dist
retention-days: 2
- name: Upload data-schemas build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-data-schemas
path: packages/data-schemas/dist
retention-days: 2
- name: Upload api build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-api
path: packages/api/dist
@ -104,16 +104,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -128,19 +128,19 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download data-schemas build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-schemas
path: packages/data-schemas/dist
- name: Download api build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-api
path: packages/api/dist
@ -162,16 +162,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -207,16 +207,16 @@ jobs:
BAN_DURATION: ${{ secrets.BAN_DURATION }}
BAN_INTERVAL: ${{ secrets.BAN_INTERVAL }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -231,19 +231,19 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download data-schemas build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-schemas
path: packages/data-schemas/dist
- name: Download api build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-api
path: packages/api/dist
@ -265,16 +265,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -289,7 +289,7 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
@ -303,16 +303,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -327,13 +327,13 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download data-schemas build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-schemas
path: packages/data-schemas/dist
@ -355,16 +355,16 @@ jobs:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -379,19 +379,19 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download data-schemas build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-schemas
path: packages/data-schemas/dist
- name: Download api build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-api
path: packages/api/dist

View file

@ -15,15 +15,15 @@ jobs:
steps:
# checkout the repo
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 'Login via Azure CLI'
uses: azure/login@v2
uses: azure/login@v3
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Build GitHub Runner container image'
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ${{ secrets.REGISTRY_LOGIN_SERVER }}
username: ${{ secrets.REGISTRY_USERNAME }}
@ -32,7 +32,7 @@ jobs:
docker build --build-arg RUNNER_VERSION=${{ env.RUNNER_VERSION }} -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/pwd9000-github-runner-lin:${{ env.RUNNER_VERSION }} .
- name: 'Push container image to ACR'
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ${{ secrets.REGISTRY_LOGIN_SERVER }}
username: ${{ secrets.REGISTRY_USERNAME }}

View file

@ -27,10 +27,10 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
@ -59,7 +59,7 @@ jobs:
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -75,7 +75,7 @@ jobs:
- name: Restore data-provider build cache
id: cache-data-provider
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-provider/dist
key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -86,7 +86,7 @@ jobs:
- name: Restore data-schemas build cache
id: cache-data-schemas
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-schemas/dist
key: build-data-schemas-${{ runner.os }}-${{ hashFiles('packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -97,7 +97,7 @@ jobs:
- name: Restore api build cache
id: cache-api
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/api/dist
key: build-api-${{ runner.os }}-${{ hashFiles('packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/tsdown.config.mjs', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json') }}

View file

@ -22,10 +22,10 @@ jobs:
outputs:
skip: ${{ steps.check.outputs.skip }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
@ -58,7 +58,7 @@ jobs:
- name: Upload package
if: steps.check.outputs.skip != 'true'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: librechat-client-package
path: npm-package/*.tgz
@ -75,7 +75,7 @@ jobs:
id-token: write # Required for OIDC trusted publishing
steps:
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
registry-url: 'https://registry.npmjs.org'
@ -84,7 +84,7 @@ jobs:
run: npm install -g npm@11.14.1 --ignore-scripts
- name: Download package
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: librechat-client-package
path: npm-package

View file

@ -21,16 +21,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -46,7 +46,7 @@ jobs:
- name: Restore data-provider build cache
id: cache-data-provider
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-provider/dist
key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -57,7 +57,7 @@ jobs:
- name: Restore data-schemas build cache
id: cache-data-schemas
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-schemas/dist
key: build-data-schemas-${{ runner.os }}-${{ hashFiles('packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -68,7 +68,7 @@ jobs:
- name: Restore api build cache
id: cache-api
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/api/dist
key: build-api-${{ runner.os }}-${{ hashFiles('packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/tsdown.config.mjs', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json') }}

View file

@ -20,8 +20,8 @@ jobs:
pack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- run: cd packages/data-provider && npm ci
@ -32,7 +32,7 @@ jobs:
cd packages/data-provider
npm pack --pack-destination "$GITHUB_WORKSPACE/npm-package"
- name: Upload package
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: librechat-data-provider-package
path: npm-package/*.tgz
@ -48,7 +48,7 @@ jobs:
contents: read
id-token: write # Required for OIDC trusted publishing
steps:
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: '24.16.0'
registry-url: 'https://registry.npmjs.org'
@ -57,7 +57,7 @@ jobs:
run: npm install -g npm@11.14.1 --ignore-scripts
- name: Download package
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: librechat-data-provider-package
path: npm-package

View file

@ -22,10 +22,10 @@ jobs:
outputs:
skip: ${{ steps.check.outputs.skip }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
@ -58,7 +58,7 @@ jobs:
- name: Upload package
if: steps.check.outputs.skip != 'true'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: librechat-data-schemas-package
path: npm-package/*.tgz
@ -75,7 +75,7 @@ jobs:
id-token: write # Required for OIDC trusted publishing
steps:
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
registry-url: 'https://registry.npmjs.org'
@ -84,7 +84,7 @@ jobs:
run: npm install -g npm@11.14.1 --ignore-scripts
- name: Download package
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: librechat-data-schemas-package
path: npm-package

View file

@ -19,7 +19,7 @@ jobs:
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev'))
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Install SSH Key
uses: shimataro/ssh-key-action@v2

View file

@ -20,10 +20,10 @@ jobs:
steps:
# checkout the repo
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: 'Login via Azure CLI'
uses: azure/login@v2
uses: azure/login@v3
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

View file

@ -40,19 +40,19 @@ jobs:
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -60,7 +60,7 @@ jobs:
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@ -78,7 +78,7 @@ jobs:
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.file }}

View file

@ -36,19 +36,19 @@ jobs:
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -56,7 +56,7 @@ jobs:
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@ -74,7 +74,7 @@ jobs:
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.file }}

View file

@ -23,19 +23,19 @@ jobs:
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -43,7 +43,7 @@ jobs:
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@ -61,7 +61,7 @@ jobs:
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.file }}

View file

@ -32,13 +32,13 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build client package target
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile.multi
@ -51,16 +51,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
# Build the real production image (final `api-build` stage), which installs
# with `npm ci --omit=dev` — the same prune that, in prod, exposed runtime
# dependencies the tsdown bundle externalizes but were never declared.
- name: Build production image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile.multi

View file

@ -24,12 +24,12 @@ jobs:
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
cache: npm

View file

@ -21,16 +21,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -45,7 +45,7 @@ jobs:
- name: Restore data-provider build cache
id: cache-data-provider
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-provider/dist
key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -56,7 +56,7 @@ jobs:
- name: Restore client-package build cache
id: cache-client-package
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/client/dist
key: build-client-package-${{ runner.os }}-${{ hashFiles('packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/tsdown.config.mjs', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -66,14 +66,14 @@ jobs:
run: npm run build:client-package
- name: Upload data-provider build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-data-provider
path: packages/data-provider/dist
retention-days: 2
- name: Upload client-package build
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: build-client-package
path: packages/client/dist
@ -85,16 +85,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -108,13 +108,13 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download client-package build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-client-package
path: packages/client/dist
@ -129,16 +129,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -152,7 +152,7 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
@ -171,16 +171,16 @@ jobs:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -194,13 +194,13 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download client-package build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-client-package
path: packages/client/dist
@ -219,16 +219,16 @@ jobs:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -242,13 +242,13 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download client-package build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-client-package
path: packages/client/dist
@ -263,16 +263,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -286,13 +286,13 @@ jobs:
run: npm ci
- name: Download data-provider build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-data-provider
path: packages/data-provider/dist
- name: Download client-package build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v7
with:
name: build-client-package
path: packages/client/dist

View file

@ -14,7 +14,7 @@ jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: supabase/embeddings-generator@v0.0.5
with:
supabase-url: ${{ secrets.SUPABASE_URL }}

View file

@ -34,7 +34,7 @@ jobs:
# sessions on a busy repo.
- name: Check for index artifact
id: check
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const { data } = await github.rest.actions.listArtifactsForRepo({

View file

@ -104,7 +104,7 @@ jobs:
image_tag: ${{ steps.tag.outputs.value }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 2
@ -127,11 +127,11 @@ jobs:
- name: Set up Docker Buildx
if: steps.changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
if: steps.changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -139,7 +139,7 @@ jobs:
- name: Build and push image
if: steps.changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch'
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .do/gitnexus
file: .do/gitnexus/Dockerfile
@ -162,7 +162,7 @@ jobs:
pull-requests: write # post deploy-complete comments on PR command dispatches
steps:
- name: Checkout deploy config
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
sparse-checkout: .do/gitnexus
fetch-depth: 1
@ -181,7 +181,7 @@ jobs:
# artifact name directly avoids the whole mess.
- name: Resolve indexes to serve
id: resolve
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const serve = []; // [{ name, artifactName, runId }]
@ -505,7 +505,7 @@ jobs:
# PR that asked for a fresh index gets a reply.
- name: Comment on PR — deploy complete
if: always()
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
MATRIX: ${{ steps.resolve.outputs.matrix }}
TRIGGER_RUN_ID: ${{ github.event.workflow_run.id }}

View file

@ -138,7 +138,7 @@ jobs:
fi
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
@ -170,7 +170,7 @@ jobs:
test -x "$RUNNER_TEMP/gitnexus-cli/node_modules/.bin/gitnexus"
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
# When the /gitnexus command dispatches us with a pr_ref, it's
# a refs/pull/<N>/head ref that GitHub mirrors into the base
@ -186,7 +186,7 @@ jobs:
# across runs so warm runs never touch HF at all.
- name: Cache HuggingFace embedding model
if: steps.flags.outputs.enable_embeddings == 'true'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/hf-cache
key: hf-model-snowflake-arctic-embed-xs-v1
@ -234,7 +234,7 @@ jobs:
echo "::endgroup::"
- name: Upload GitNexus index
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
# Artifact naming order of precedence:
# 1. /gitnexus command dispatch: inputs.pr_number -> pr-<N>
@ -270,7 +270,7 @@ jobs:
# a PR number can still opt into a deploy by setting deploy_after=true.
- name: Trigger deploy workflow after non-PR dispatches
if: inputs.deploy_after && inputs.pr_number == '' && needs.index.result == 'success'
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
core.info('deploy_after=true; dispatching gitnexus-deploy.yml manually.');
@ -289,7 +289,7 @@ jobs:
# inputs.pr_number is set and reports the index job result.
- name: Comment on PR — index complete
if: inputs.pr_number != ''
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
EMBEDDINGS_INPUT: ${{ inputs.embeddings }}
INDEX_RESULT: ${{ needs.index.result }}

View file

@ -56,7 +56,7 @@ jobs:
steps:
- name: Parse command and resolve PR head ref
id: parse
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const body = context.payload.comment.body.trim();
@ -93,7 +93,7 @@ jobs:
);
- name: Dispatch gitnexus-index workflow
uses: actions/github-script@v7
uses: actions/github-script@v8
env:
EMBEDDINGS: ${{ steps.parse.outputs.embeddings }}
PR_NUMBER: ${{ steps.parse.outputs.pr_number }}
@ -130,7 +130,7 @@ jobs:
});
- name: React to the comment
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
await github.rest.reactions.createForIssueComment({

View file

@ -48,7 +48,7 @@ jobs:
} >> "$GITHUB_OUTPUT"
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false
@ -60,7 +60,7 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Install Helm
uses: azure/setup-helm@v4
uses: azure/setup-helm@v5
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
@ -73,7 +73,7 @@ jobs:
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}

View file

@ -23,7 +23,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Find unused i18next keys
id: find-unused

View file

@ -21,10 +21,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version-file: otel/langfuse-fanout/go.mod
cache-dependency-path: otel/langfuse-fanout/go.sum

View file

@ -17,12 +17,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set Up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
@ -53,7 +53,7 @@ jobs:
steps:
# 1. Check out the repository.
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
persist-credentials: false
@ -68,7 +68,7 @@ jobs:
# workflow does not depend on the global GITHUB_TOKEN PR-creation setting.
- name: Create Pull Request
id: create-pull-request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.LOCIZE_PR_TOKEN }}
add-paths: |

View file

@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: main
fetch-depth: 0
@ -46,15 +46,15 @@ jobs:
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -62,7 +62,7 @@ jobs:
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@ -74,7 +74,7 @@ jobs:
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.file }}

View file

@ -55,16 +55,16 @@ jobs:
E2E_STREAM_STORE: ${{ matrix.stream_store }}
REDIS_URI: redis://127.0.0.1:6379
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
- name: Restore node_modules cache
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
node_modules
@ -82,7 +82,7 @@ jobs:
- name: Restore data-provider build cache
id: cache-data-provider
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-provider/dist
key: build-data-provider-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -93,7 +93,7 @@ jobs:
- name: Restore data-schemas build cache
id: cache-data-schemas
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/data-schemas/dist
key: build-data-schemas-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -104,7 +104,7 @@ jobs:
- name: Restore api build cache
id: cache-api
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/api/dist
key: build-api-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/tsdown.config.mjs', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/tsdown.config.mjs', 'packages/data-schemas/package.json') }}
@ -115,7 +115,7 @@ jobs:
- name: Restore client-package build cache
id: cache-client-package
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: packages/client/dist
key: build-client-package-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/tsdown.config.mjs', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -126,7 +126,7 @@ jobs:
- name: Restore client app build cache
id: cache-client-app
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: client/dist
key: build-client-app-e2e-${{ runner.os }}-${{ hashFiles('package-lock.json', 'client/src/**', 'client/public/**', 'client/scripts/post-build.cjs', 'client/index.html', 'client/package.json', 'client/vite.config.*', 'client/tsconfig*.json', 'client/tailwind.config.*', 'client/postcss.config.*', 'packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/tsdown.config.mjs', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/tsdown.config.mjs', 'packages/data-provider/package.json') }}
@ -148,7 +148,7 @@ jobs:
- name: Upload Playwright HTML report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: playwright-report-${{ matrix.stream_store }}-${{ matrix.shard }}
path: e2e/playwright-report/**
@ -157,7 +157,7 @@ jobs:
- name: Upload traces & screenshots
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: playwright-test-results-${{ matrix.stream_store }}-${{ matrix.shard }}
path: e2e/specs/.test-results/**

View file

@ -25,7 +25,7 @@ jobs:
steps:
# Check out the repository
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Validate release tag
id: release-tag
@ -48,15 +48,15 @@ jobs:
# Set up QEMU
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v4
# Set up Docker Buildx
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
# Log in to GitHub Container Registry
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -64,7 +64,7 @@ jobs:
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@ -104,7 +104,7 @@ jobs:
# Build and push Docker images for each target
- name: Build and push Docker images
uses: docker/build-push-action@v5
uses: docker/build-push-action@v7
with:
context: .
file: ${{ matrix.file }}

View file

@ -19,10 +19,10 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use Node.js 24.16.0
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: '24.16.0'
cache: 'npm'

View file

@ -1,7 +1,7 @@
import { logger } from '@librechat/data-schemas';
import { observeRedisOperation, RedisUseCases } from '~/cache/redisTelemetry';
import { instrumentIORedisClient, RedisUseCases } from '~/cache/redisTelemetry';
import { cacheConfig as cache } from '~/cache/cacheConfig';
import { keyvRedisClient } from '~/cache/redisClients';
import { ioredisClient } from '~/cache/redisClients';
import { clusterConfig as cluster } from './config';
/**
@ -19,10 +19,18 @@ import { clusterConfig as cluster } from './config';
* - If leader crashes, the lease eventually expires, and the key disappears
* - On shutdown, leader deletes its key to allow immediate re-election
* - Followers check for leadership and attempt to claim it when the key is empty
*
* Uses ioredis (not @keyv/redis) for all lock operations. ioredis applies REDIS_KEY_PREFIX via
* keyPrefix and correctly retries MOVED/ASK redirects on Redis Cluster for SET NX and Lua EVAL.
* @keyv/redis EVAL has been observed to surface unhandled MOVED errors on cluster, leaving the
* leadership key stuck after resign().
*/
export class LeaderElection {
// We can't use Keyv namespace here because we need direct Redis access for atomic operations
static readonly LEADER_KEY: string = `${cache.REDIS_KEY_PREFIX}${cache.GLOBAL_PREFIX_SEPARATOR}LeadingServerUUID`;
/**
* Logical leadership key. ioredis prepends REDIS_KEY_PREFIX via its keyPrefix option,
* so the on-wire key is `{REDIS_KEY_PREFIX}::LeadingServerUUID`.
*/
static readonly LEADER_KEY: string = 'LeadingServerUUID';
private static _instance = new LeaderElection();
readonly UUID: string = crypto.randomUUID();
@ -38,6 +46,13 @@ export class LeaderElection {
LeaderElection._instance = this;
}
private static redis() {
if (!ioredisClient) {
throw new Error('Redis client is not initialized');
}
return instrumentIORedisClient(ioredisClient, RedisUseCases.LEADER_ELECTION);
}
/**
* Checks if this instance is the current leader.
* If no leader exists, waits upto 2 seconds (randomized to avoid thundering herd) then attempts self-election.
@ -80,12 +95,7 @@ export class LeaderElection {
end
`;
await observeRedisOperation('keyv', RedisUseCases.LEADER_ELECTION, 'eval', () =>
keyvRedisClient!.eval(script, {
keys: [LeaderElection.LEADER_KEY],
arguments: [this.UUID],
}),
);
await LeaderElection.redis().eval(script, 1, LeaderElection.LEADER_KEY, this.UUID);
} catch (error) {
logger.error('Failed to release leadership lock:', error);
}
@ -98,9 +108,7 @@ export class LeaderElection {
*/
public static async getLeaderUUID(): Promise<string | null> {
if (!cache.USE_REDIS) return null;
return await observeRedisOperation('keyv', RedisUseCases.LEADER_ELECTION, 'get', () =>
keyvRedisClient!.get(LeaderElection.LEADER_KEY),
);
return await LeaderElection.redis().get(LeaderElection.LEADER_KEY);
}
/**
@ -120,11 +128,12 @@ export class LeaderElection {
*/
private async electSelf(): Promise<boolean> {
try {
const result = await observeRedisOperation('keyv', RedisUseCases.LEADER_ELECTION, 'set', () =>
keyvRedisClient!.set(LeaderElection.LEADER_KEY, this.UUID, {
NX: true,
EX: cluster.LEADER_LEASE_DURATION,
}),
const result = await LeaderElection.redis().set(
LeaderElection.LEADER_KEY,
this.UUID,
'EX',
cluster.LEADER_LEASE_DURATION,
'NX',
);
if (result !== 'OK') return false;
@ -159,15 +168,12 @@ export class LeaderElection {
end
`;
const result = await observeRedisOperation(
'keyv',
RedisUseCases.LEADER_ELECTION,
'eval',
() =>
keyvRedisClient!.eval(script, {
keys: [LeaderElection.LEADER_KEY],
arguments: [this.UUID, cluster.LEADER_LEASE_DURATION.toString()],
}),
const result = await LeaderElection.redis().eval(
script,
1,
LeaderElection.LEADER_KEY,
this.UUID,
cluster.LEADER_LEASE_DURATION.toString(),
);
if (result === 0) {

View file

@ -6,6 +6,13 @@ describe('LeaderElection with Redis', () => {
let keyvRedisClient: Awaited<typeof import('~/cache/redisClients')>['keyvRedisClient'];
let ioredisClient: Awaited<typeof import('~/cache/redisClients')>['ioredisClient'];
const clearLeaderKey = async () => {
// LeaderElection uses ioredis (keyPrefix applied). Match that client for cleanup.
if (ioredisClient) {
await ioredisClient.del(LeaderElection.LEADER_KEY);
}
};
beforeAll(async () => {
// Set up environment variables for Redis
process.env.USE_REDIS = 'true';
@ -20,22 +27,42 @@ describe('LeaderElection with Redis', () => {
keyvRedisClient = redisClients.keyvRedisClient;
ioredisClient = redisClients.ioredisClient;
// Ensure Redis is connected
// Ensure Redis is connected (both clients; LeaderElection uses ioredis)
if (!ioredisClient) {
throw new Error('ioredis client is not initialized');
}
if (!keyvRedisClient) {
throw new Error('Redis client is not initialized');
}
// Wait for connection and topology discovery to complete
await redisClients.keyvRedisClientReady;
const redis = ioredisClient;
if (redis.status !== 'ready') {
await new Promise<void>((resolve, reject) => {
const onReady = () => {
cleanup();
resolve();
};
const onError = (err: Error) => {
cleanup();
reject(err);
};
const cleanup = () => {
redis.off('ready', onReady);
redis.off('error', onError);
};
redis.once('ready', onReady);
redis.once('error', onError);
});
}
// Increase max listeners to handle many instances in tests
process.setMaxListeners(200);
});
beforeEach(async () => {
if (keyvRedisClient) {
await keyvRedisClient.del(LeaderElection.LEADER_KEY);
}
await clearLeaderKey();
new LeaderElection().clearRefreshTimer();
});
@ -44,9 +71,7 @@ describe('LeaderElection with Redis', () => {
await Promise.all(instances.map((instance) => instance.resign()));
} finally {
instances = [];
if (keyvRedisClient) {
await keyvRedisClient.del(LeaderElection.LEADER_KEY);
}
await clearLeaderKey();
}
});