Skip to content

Commit

Permalink
Node: add OBJECT IDLETIME command
Browse files Browse the repository at this point in the history
  • Loading branch information
aaron-congo committed Jun 13, 2024
1 parent 2c8b8ff commit be33f03
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 @@ -22,6 +22,7 @@
* Node: Added OBJECT FREQ command ([#1542](https://github.com/aws/glide-for-redis/pull/1542))
* Node: Added LINSERT command ([#1544](https://github.com/aws/glide-for-redis/pull/1544))
* Node: Added XLEN command ([#1555](https://github.com/aws/glide-for-redis/pull/1555))
* 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 @@ -100,6 +100,7 @@ import {
createZScore,
createSUnionStore,
createXLen,
createObjectIdletime,
} from "./Commands";
import {
ClosingError,
Expand Down Expand Up @@ -2550,6 +2551,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 @@ -1476,3 +1476,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 @@ -105,6 +105,7 @@ import {
createZScore,
createSUnionStore,
createXLen,
createObjectIdletime,
} from "./Commands";
import { redis_request } from "./ProtobufMessage";

Expand Down Expand Up @@ -1475,6 +1476,19 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
public object_freq(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 @@ -365,4 +365,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 @@ -3211,6 +3211,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 be33f03

Please sign in to comment.