Skip to content
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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ x-pack/solutions/security/plugins/lists @elastic/security-detection-engine
x-pack/platform/plugins/shared/ai_infra/llm_tasks @elastic/appex-ai-infra
examples/locator_examples @elastic/appex-sharedux
examples/locator_explorer @elastic/appex-sharedux
packages/kbn-lock-manager @elastic/obs-ai-assistant
src/platform/packages/shared/kbn-logging @elastic/kibana-core
src/platform/packages/shared/kbn-logging-mocks @elastic/kibana-core
x-pack/platform/plugins/shared/logs_data_access @elastic/obs-ux-logs-team
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,7 @@
"@kbn/llm-tasks-plugin": "link:x-pack/platform/plugins/shared/ai_infra/llm_tasks",
"@kbn/locator-examples-plugin": "link:examples/locator_examples",
"@kbn/locator-explorer-plugin": "link:examples/locator_explorer",
"@kbn/lock-manager": "link:packages/kbn-lock-manager",
"@kbn/logging": "link:src/platform/packages/shared/kbn-logging",
"@kbn/logging-mocks": "link:src/platform/packages/shared/kbn-logging-mocks",
"@kbn/logs-data-access-plugin": "link:x-pack/platform/plugins/shared/logs_data_access",
Expand Down
54 changes: 54 additions & 0 deletions packages/kbn-lock-manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Kibana Lock Manager

A simple, distributed lock manager built on top of Elasticsearch.
Ensures that only one process at a time can hold a named lock, with automatic lease renewal and token fencing for safe release.

# API Documentation

## `withLock<T>(lockId, callback, options)`

Acquires a lock and executes the provided callback. If the lock is already held by another process, the method will throw a `LockAcquisitionError` and the callback will not be executed. When the callback returns the lock is released.

### Parameters

- **`lockId`** (`string`): Unique identifier for the lock

- **`callback`** (`() => Promise<T>`): Asynchronous function to execute once the lock is acquired. This function will be executed only if the lock acquisition succeeds.

- **`options`** (`object`, optional): Additional configuration options.
- **`metadata`** (`Record<string, any>`, optional): Custom metadata to store with the lock.

## Example

```ts
import { LockManagerService, LockAcquisitionError } from '@kbn/lock-manager';


async function reIndexWithLock() {
// Attempt to acquire "my_lock"; if successful, runs the callback.
const lmService = new LockManagerService(coreSetup, logger);
return lmService.withLock('my_lock', async () => {
// …perform your exclusive operation here…
});
}

reIndexWithLock().catch((err) => {
if (err instanceof LockAcquisitionError) {
logger.debug('Re-index already in progress, skipping.');
return;
}
logger.error(`Failed to re-index: ${err.message}`);
});
```

## How It Works
**Atomic Acquire**
Performs one atomic Elasticsearch update that creates a new lock or renews an existing one - so if multiple processes race for the same lock, only one succeeds.

**TTL-Based Lease**
Each lock has a short, fixed lifespan (default 30s) and will automatically expire if not renewed. While the callback is executing, the lock will automatically extend the TTL to keep the lock active. This safeguards against deadlocks because if a Kibana node crashes after having obtained a lock it will automatically be released after 30 seconds.

Note: If Kibana node crashes, another process could acquire the same lock and start that task again when the lock automatically expires. To prevent your operation from running multiple times, include an application-level check (for example, querying Elasticsearch or your own status flag) to verify the operation isn’t already in progress before proceeding.

**Token Fencing**
Each lock operation carries a unique token. Only the process with the matching token can extend or release the lock, preventing stale holders from interfering.
11 changes: 11 additions & 0 deletions packages/kbn-lock-manager/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export { LockAcquisitionError } from './src/lock_manager_client';
export { LockManagerService } from './src/lock_manager_service';
14 changes: 14 additions & 0 deletions packages/kbn-lock-manager/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

module.exports = {
preset: '@kbn/test/jest_node',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-lock-manager'],
};
6 changes: 6 additions & 0 deletions packages/kbn-lock-manager/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "shared-server",
"id": "@kbn/lock-manager",
"owner": ["@elastic/obs-ai-assistant"],
"devOnly": false
}
6 changes: 6 additions & 0 deletions packages/kbn-lock-manager/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@kbn/lock-manager",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0"
}
Loading