diff --git a/packages/kbn-lock-manager/src/lock_manager_client.ts b/packages/kbn-lock-manager/src/lock_manager_client.ts index 0db293a0d0b4..350711cad426 100644 --- a/packages/kbn-lock-manager/src/lock_manager_client.ts +++ b/packages/kbn-lock-manager/src/lock_manager_client.ts @@ -87,6 +87,8 @@ export class LockManager { def instantNow = Instant.ofEpochMilli(now); ctx._source.createdAt = instantNow.toString(); ctx._source.expiresAt = instantNow.plusMillis(params.ttl).toString(); + ctx._source.metadata = params.metadata; + ctx._source.token = params.token; } else { ctx.op = 'noop'; } @@ -94,13 +96,11 @@ export class LockManager { params: { ttl, token: this.token, + metadata, }, }, // @ts-expect-error - upsert: { - metadata, - token: this.token, - }, + upsert: {}, }, { retryOnTimeout: true, @@ -189,7 +189,7 @@ export class LockManager { this.logger.debug(`Lock "${this.lockId}" released with token ${this.token}.`); return true; case 'noop': - this.logger.debug( + this.logger.warn( `Lock "${this.lockId}" with token = ${this.token} could not be released. Token does not match.` ); return false; diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/distributed_lock_manager/distributed_lock_manager.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/distributed_lock_manager/distributed_lock_manager.spec.ts index ef6a9c26cb29..ad504d546e84 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/distributed_lock_manager/distributed_lock_manager.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/ai_assistant/distributed_lock_manager/distributed_lock_manager.spec.ts @@ -236,15 +236,41 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon expect(lock?.metadata).to.eql({ attempt: 'one' }); }); - it('allows re-acquisition after expiration', async () => { - // Acquire with a very short TTL. - const acquired = await manager1.acquire({ ttl: 500, metadata: { attempt: 'one' } }); - expect(acquired).to.be(true); + describe('when a lock by "manager1" expires, and is attempted re-acquired by "manager2"', () => { + let expiredLock: LockDocument | undefined; + let reacquireResult: boolean; + beforeEach(async () => { + // Acquire with a very short TTL. + const acquired = await manager1.acquire({ ttl: 500, metadata: { attempt: 'one' } }); + expect(acquired).to.be(true); + await sleep(1000); // wait for lock to expire + expiredLock = await getLockById(es, LOCK_ID); + reacquireResult = await manager2.acquire({ metadata: { attempt: 'two' } }); + }); - await sleep(1000); // wait for lock to expire + it('can be re-acquired', async () => { + expect(reacquireResult).to.be(true); + }); - const reacquired = await manager2.acquire({ metadata: { attempt: 'two' } }); - expect(reacquired).to.be(true); + it('updates the token when re-acquired', async () => { + const reacquiredLock = await getLockById(es, LOCK_ID); + expect(expiredLock?.token).not.to.be(reacquiredLock?.token); + }); + + it('updates the metadata when re-acquired', async () => { + const reacquiredLock = await getLockById(es, LOCK_ID); + expect(reacquiredLock?.metadata).to.eql({ attempt: 'two' }); + }); + + it('cannot be released by "manager1"', async () => { + const res = await manager1.release(); + expect(res).to.be(false); + }); + + it('can be released by "manager2"', async () => { + const res = await manager2.release(); + expect(res).to.be(true); + }); }); });