Skip to content

Commit

Permalink
Node: add OBJECT IDLETIME command (valkey-io#1567)
Browse files Browse the repository at this point in the history
  • Loading branch information
aaron-congo authored Jun 13, 2024
1 parent 832de8a commit 39bc778
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* Node: Added XLEN command ([#1555](https://github.com/aws/glide-for-redis/pull/1555))
* Node: Added ZINTERCARD command ([#1553](https://github.com/aws/glide-for-redis/pull/1553))
* Python: Added LMPOP and BLMPOP commands ([#1547](https://github.com/aws/glide-for-redis/pull/1547))
* Node: Added OBJECT IDLETIME command ([#1567](https://github.com/aws/glide-for-redis/pull/1567))

### Breaking Changes
* Node: Update XREAD to return a Map of Map ([#1494](https://github.com/aws/glide-for-redis/pull/1494))
Expand Down
19 changes: 19 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import {
createSUnionStore,
createXLen,
createZInterCard,
createObjectIdletime,
} from "./Commands";
import {
ClosingError,
Expand Down Expand Up @@ -2574,6 +2575,24 @@ export class BaseClient {
return this.createWritePromise(createObjectFreq(key));
}

/**
* Returns the time in seconds since the last access to the value stored at `key`.
*
* See https://valkey.io/commands/object-idletime/ for more details.
*
* @param key - The key of the object to get the idle time of.
* @returns If `key` exists, returns the idle time in seconds. Otherwise, returns `null`.
*
* @example
* ```typescript
* const result = await client.objectIdletime("my_hash");
* console.log(result); // Output: 13 - "my_hash" was last accessed 13 seconds ago.
* ```
*/
public objectIdletime(key: string): Promise<number | null> {
return this.createWritePromise(createObjectIdletime(key));
}

/**
* @internal
*/
Expand Down
7 changes: 7 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1493,3 +1493,10 @@ export function createObjectEncoding(key: string): redis_request.Command {
export function createObjectFreq(key: string): redis_request.Command {
return createCommand(RequestType.ObjectFreq, [key]);
}

/**
* @internal
*/
export function createObjectIdletime(key: string): redis_request.Command {
return createCommand(RequestType.ObjectIdleTime, [key]);
}
14 changes: 14 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import {
createSUnionStore,
createXLen,
createZInterCard,
createObjectIdletime,
} from "./Commands";
import { redis_request } from "./ProtobufMessage";

Expand Down Expand Up @@ -1493,6 +1494,19 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
public objectFreq(key: string): T {
return this.addAndReturn(createObjectFreq(key));
}

/**
* Returns the time in seconds since the last access to the value stored at `key`.
*
* See https://valkey.io/commands/object-idletime/ for more details.
*
* @param key - The key of the object to get the idle time of.
*
* Command Response - If `key` exists, returns the idle time in seconds. Otherwise, returns `null`.
*/
public objectIdletime(key: string): T {
return this.addAndReturn(createObjectIdletime(key));
}
}

/**
Expand Down
45 changes: 45 additions & 0 deletions node/tests/RedisClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,51 @@ describe("RedisClient", () => {
},
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
"object idletime transaction test_%p",
async (protocol) => {
const client = await RedisClient.createClient(
getClientConfigurationOption(cluster.getAddresses(), protocol),
);

const key = uuidv4();
const maxmemoryPolicyKey = "maxmemory-policy";
const config = await client.configGet([maxmemoryPolicyKey]);
const maxmemoryPolicy = String(config[maxmemoryPolicyKey]);

try {
const transaction = new Transaction();
transaction.configSet({
// OBJECT IDLETIME requires a non-LFU maxmemory-policy
[maxmemoryPolicyKey]: "allkeys-random",
});
transaction.set(key, "foo");
transaction.objectIdletime(key);

const response = await client.exec(transaction);
expect(response).not.toBeNull();

if (response != null) {
expect(response.length).toEqual(3);
// transaction.configSet({[maxmemoryPolicyKey]: "allkeys-random"});
expect(response[0]).toEqual("OK");
// transaction.set(key, "foo");
expect(response[1]).toEqual("OK");
// transaction.objectIdletime(key);
expect(response[2]).toBeGreaterThanOrEqual(0);
}
} finally {
expect(
await client.configSet({
[maxmemoryPolicyKey]: maxmemoryPolicy,
}),
).toEqual("OK");
}

client.close();
},
);

runBaseTests<Context>({
init: async (protocol, clientName?) => {
const options = getClientConfigurationOption(
Expand Down
45 changes: 45 additions & 0 deletions node/tests/RedisClusterClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,49 @@ describe("RedisClusterClient", () => {
client.close();
},
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
"object idletime transaction test_%p",
async (protocol) => {
const client = await RedisClusterClient.createClient(
getClientConfigurationOption(cluster.getAddresses(), protocol),
);

const key = uuidv4();
const maxmemoryPolicyKey = "maxmemory-policy";
const config = await client.configGet([maxmemoryPolicyKey]);
const maxmemoryPolicy = String(config[maxmemoryPolicyKey]);

try {
const transaction = new ClusterTransaction();
transaction.configSet({
// OBJECT IDLETIME requires a non-LFU maxmemory-policy
[maxmemoryPolicyKey]: "allkeys-random",
});
transaction.set(key, "foo");
transaction.objectIdletime(key);

const response = await client.exec(transaction);
expect(response).not.toBeNull();

if (response != null) {
expect(response.length).toEqual(3);
// transaction.configSet({[maxmemoryPolicyKey]: "allkeys-random"});
expect(response[0]).toEqual("OK");
// transaction.set(key, "foo");
expect(response[1]).toEqual("OK");
// transaction.objectIdletime(key);
expect(response[2]).toBeGreaterThanOrEqual(0);
}
} finally {
expect(
await client.configSet({
[maxmemoryPolicyKey]: maxmemoryPolicy,
}),
).toEqual("OK");
}

client.close();
},
);
});
43 changes: 43 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3254,6 +3254,49 @@ export function runBaseTests<Context>(config: {
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
"object idletime test_%p",
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key = uuidv4();
const nonExistingKey = uuidv4();
const maxmemoryPolicyKey = "maxmemory-policy";
const config = await client.configGet([maxmemoryPolicyKey]);
const maxmemoryPolicy = String(config[maxmemoryPolicyKey]);

try {
expect(
await client.configSet({
// OBJECT IDLETIME requires a non-LFU maxmemory-policy
[maxmemoryPolicyKey]: "allkeys-random",
}),
).toEqual("OK");
expect(await client.objectIdletime(nonExistingKey)).toEqual(
null,
);
expect(await client.set(key, "foobar")).toEqual("OK");

await wait(2000);

expect(await client.objectIdletime(key)).toBeGreaterThan(0);
} finally {
expect(
await client.configSet({
[maxmemoryPolicyKey]: maxmemoryPolicy,
}),
).toEqual("OK");
}
}, protocol);
},
config.timeout,
);

function wait(numMilliseconds: number) {
return new Promise((resolve) => {
setTimeout(resolve, numMilliseconds);
});
}
}

export function runCommonTests<Context>(config: {
Expand Down

0 comments on commit 39bc778

Please sign in to comment.