Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: modernise, clean-up and update #132

Merged
merged 1 commit into from
Jul 14, 2023
Merged
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
13 changes: 2 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -24,17 +24,8 @@ jobs:
- name: Setup Deno
uses: denoland/setup-deno@v1

- name: Verify formatting
run: deno fmt --check

- name: Run linter
run: deno lint

- name: Run tests
run: deno task test

- name: Generate lcov
run: deno task coverage
- name: Verify formatting, run linter, run tests and generate lcov
run: deno task ok

- name: Upload coverage
uses: codecov/codecov-action@v3
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -197,19 +197,15 @@ const redisConn = await retry(async () => await Deno.connect({ port: 6379 }));
## Contributing

Before submitting a pull request, please run:

1. `deno fmt`
2. `deno lint`
3. `deno task test:dev` and ensure all tests pass
4. `deno task bench:dev` and ensure performance hasn't degraded
Before submitting a pull request, please run `deno task ok:dev`. This task
checks formatting, runs the linter and runs tests.

> Note: Redis must be installed on your local machine. For installation
> instructions, see [here](https://redis.io/docs/getting-started/installation/).
## Comparison

Data recorded on November 28, 2022.
Data recorded on July 14, 2023.

### Benchmarks

@@ -221,9 +217,9 @@ Data recorded on November 28, 2022.

| Module | Size (KB) | Dependencies |
| ----------- | --------- | ------------ |
| r2d2 | 17.24 | 2 |
| deno-redis | 187.76 | 25 |
| npm:ioredis | 890.96 | 10 |
| npm:redis | 891.60 | 9 |
| r2d2 | 24.46 | 5 |
| deno-redis | 169.04 | 25 |
| npm:ioredis | 894.69 | 10 |
| npm:redis | 934.27 | 9 |

> Note: Results were produced using `deno info <module>`
66 changes: 33 additions & 33 deletions bench.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"runtime": "Deno/1.32.5 aarch64-apple-darwin",
"runtime": "Deno/1.35.1 aarch64-apple-darwin",
"cpu": "Apple M2",
"benches": [
{
@@ -10,14 +10,14 @@
"results": [
{
"ok": {
"n": 2796,
"min": 110958.0,
"max": 485750.0,
"avg": 179484.0,
"p75": 183333.0,
"p99": 309333.0,
"p995": 343042.0,
"p999": 390666.0
"n": 2836,
"min": 110083.0,
"max": 465750.0,
"avg": 176906.0,
"p75": 180917.0,
"p99": 310500.0,
"p995": 317375.0,
"p999": 362292.0
}
}
]
@@ -30,14 +30,14 @@
"results": [
{
"ok": {
"n": 2095,
"min": 192209.0,
"max": 4666583.0,
"avg": 239831.0,
"p75": 239833.0,
"p99": 374250.0,
"p995": 613541.0,
"p999": 687667.0
"n": 2240,
"min": 173375.0,
"max": 3772833.0,
"avg": 224189.0,
"p75": 227208.0,
"p99": 356833.0,
"p995": 558916.0,
"p999": 673708.0
}
}
]
@@ -50,14 +50,14 @@
"results": [
{
"ok": {
"n": 1218,
"min": 275541.0,
"max": 3230083.0,
"avg": 413316.0,
"p75": 331750.0,
"p99": 2574416.0,
"p995": 2805291.0,
"p999": 3229417.0
"n": 2683,
"min": 149917.0,
"max": 4179709.0,
"avg": 186997.0,
"p75": 184500.0,
"p99": 312125.0,
"p995": 511125.0,
"p999": 866375.0
}
}
]
@@ -70,14 +70,14 @@
"results": [
{
"ok": {
"n": 727,
"min": 362417.0,
"max": 4682334.0,
"avg": 698751.0,
"p75": 566000.0,
"p99": 3368292.0,
"p995": 3912583.0,
"p999": 4682334.0
"n": 1790,
"min": 200042.0,
"max": 780292.0,
"avg": 280908.0,
"p75": 284458.0,
"p99": 393541.0,
"p995": 459833.0,
"p999": 688834.0
}
}
]
6 changes: 3 additions & 3 deletions bench.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { connect } from "https://deno.land/x/redis@v0.29.3/mod.ts";
import { Redis } from "npm:ioredis@5.3.1";
import nodeRedis from "npm:redis@4.6.5";
import { connect } from "https://deno.land/x/redis@v0.30.0/mod.ts";
import { Redis } from "npm:ioredis@5.3.2";
import nodeRedis from "npm:redis@4.6.7";

import { pipelineCommands, sendCommand } from "./mod.ts";

14 changes: 6 additions & 8 deletions deno.json
Original file line number Diff line number Diff line change
@@ -9,13 +9,11 @@
"bench": "deno bench --allow-net --allow-env --json > bench.json",
"bench:dev": "deno task redis:start && deno task bench",
"coverage": "deno coverage cov --exclude='test.ts' --lcov --output=cov.lcov",
"update:dev": "deno run -A https://deno.land/x/udd/main.ts --test=\"deno task test:dev\" mod.ts test.ts"
"ok": "deno fmt --check && deno lint && deno task test && deno task coverage",
"ok:dev": "deno fmt --check && deno lint && deno task test:dev",
"update:dev": "deno run -A https://deno.land/x/udd/main.ts --test=\"deno task test:dev\" mod.ts test.ts bench.ts"
},
"fmt": {
"files": {
"exclude": [
"cov"
]
}
}
"exclude": [
"/cov"
]
}
38 changes: 14 additions & 24 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -4,9 +4,7 @@ import { BytesList } from "https://deno.land/std@0.194.0/bytes/bytes_list.ts";
import { writeAll } from "https://deno.land/std@0.194.0/streams/write_all.ts";
import { readDelim } from "https://deno.land/std@0.194.0/io/read_delim.ts";

/** Redis command */
export type Command = (string | number | Uint8Array)[];
/** Redis reply */
export type Reply =
| string
| number
@@ -44,8 +42,6 @@ const STREAMED_REPLY_START_DELIMITER = "?".charCodeAt(0);
const STREAMED_STRING_END_DELIMITER = ";0";
const STREAMED_AGGREGATE_END_DELIMITER = ".";

/** 1. Request */

function createRawRequest(command: Command): Uint8Array {
const lines = new BytesList();
lines.add(encoder.encode(ARRAY_PREFIX_STRING + command.length + CRLF));
@@ -74,7 +70,7 @@ function createStringRequest(command: (string | number)[]): Uint8Array {
/**
* Transforms a command, which is an array of arguments, into an RESP request.
*
* See {@link https://redis.io/docs/reference/protocol-spec/#send-commands-to-a-redis-server}
* @see {@link https://redis.io/docs/reference/protocol-spec/#send-commands-to-a-redis-server}
*/
function createRequest(command: Command): Uint8Array {
return command.some((arg) => arg instanceof Uint8Array)
@@ -83,9 +79,9 @@ function createRequest(command: Command): Uint8Array {
}

/**
* Just writes a command to the Redis server.
* Just writes a command to the Redis server without listening for a reply.
*
* Example:
* @example
* ```ts
* import { writeCommand } from "https://deno.land/x/r2d2@$VERSION/mod.ts";
*
@@ -101,8 +97,6 @@ export async function writeCommand(
await writeAll(writer, createRequest(command));
}

/** 2. Reply */

function removePrefix(line: Uint8Array): string {
return decoder.decode(line.slice(1));
}
@@ -147,7 +141,7 @@ async function readArray(
line: Uint8Array,
iterator: AsyncIterableIterator<Uint8Array>,
): Promise<null | Reply[]> {
const length = readNumber(line);
const length = readNumberOrDouble(line);
return length === -1 ? null : await readNReplies(length, iterator);
}

@@ -181,13 +175,12 @@ function readBoolean(line: Uint8Array): boolean {
return removePrefix(line) === "t";
}

/** Also reads verbatim string */
async function readBulkString(
async function readBulkOrVerbatimString(
line: Uint8Array,
iterator: AsyncIterableIterator<Uint8Array>,
raw = false,
): Promise<string | null> {
if (readNumber(line) === -1) {
if (readNumberOrDouble(line) === -1) {
return null;
}
const { value } = await iterator.next();
@@ -202,13 +195,12 @@ async function readMap(
line: Uint8Array,
iterator: AsyncIterableIterator<Uint8Array>,
): Promise<Record<string, any>> {
const length = readNumber(line) * 2;
const length = readNumberOrDouble(line) * 2;
const array = await readNReplies(length, iterator);
return toObject(array);
}

/** Reads an integer or double */
function readNumber(line: Uint8Array): number {
function readNumberOrDouble(line: Uint8Array): number {
const number = removePrefix(line);
switch (number) {
case "inf":
@@ -265,7 +257,7 @@ async function readStreamedString(
/**
* Reads and processes the response line-by-line.
*
* See {@link https://redis.io/docs/reference/protocol-spec/#resp-protocol-description}
* @see {@link https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md}
*/
export async function readReply(
iterator: AsyncIterableIterator<Uint8Array>,
@@ -293,10 +285,10 @@ export async function readReply(
case VERBATIM_STRING_PREFIX:
return isSteamedReply(value)
? await readStreamedString(iterator)
: await readBulkString(value, iterator, raw);
: await readBulkOrVerbatimString(value, iterator, raw);
case DOUBLE_PREFIX:
case INTEGER_PREFIX:
return readNumber(value);
return readNumberOrDouble(value);
case ERROR_PREFIX:
return readError(value);
case MAP_PREFIX:
@@ -317,12 +309,10 @@ export async function readReply(
}
}

/** 3. Combined */

/**
* Sends a command to the Redis server and returns the parsed reply.
*
* Example:
* @example
* ```ts
* import { sendCommand } from "https://deno.land/x/r2d2@$VERSION/mod.ts";
*
@@ -347,7 +337,7 @@ export async function sendCommand(
/**
* Pipelines commands to the Redis server and returns the parsed replies.
*
* Example:
* @example
* ```ts
* import { pipelineCommands } from "https://deno.land/x/r2d2@$VERSION/mod.ts";
*
@@ -377,7 +367,7 @@ export async function pipelineCommands(
/**
* Used for pub/sub. Listens for replies from the Redis server.
*
* Example:
* @example
* ```ts
* import { writeCommand, readReplies } from "https://deno.land/x/r2d2@$VERSION/mod.ts";
*
8 changes: 4 additions & 4 deletions test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
assertEquals,
assertRejects,
} from "https://deno.land/std@0.184.0/testing/asserts.ts";
import { StringReader } from "https://deno.land/std@0.184.0/io/string_reader.ts";
import { StringWriter } from "https://deno.land/std@0.184.0/io/string_writer.ts";
import { readDelim } from "https://deno.land/std@0.184.0/io/read_delim.ts";
} from "https://deno.land/std@0.194.0/testing/asserts.ts";
import { StringReader } from "https://deno.land/std@0.194.0/io/string_reader.ts";
import { StringWriter } from "https://deno.land/std@0.194.0/io/string_writer.ts";
import { readDelim } from "https://deno.land/std@0.194.0/io/read_delim.ts";

import {
type Command,