From 04e68ac4ade14d68809ca58d7ad8536eceda2b1e Mon Sep 17 00:00:00 2001 From: Zihua Li Date: Sun, 6 Mar 2022 23:38:54 +0800 Subject: [PATCH] fix: remove dropBufferSupport option --- README.md | 96 +++++++++++----- lib/DataHandler.ts | 3 +- lib/Pipeline.ts | 6 +- lib/connectors/SentinelConnector/index.ts | 1 - lib/redis/RedisOptions.ts | 2 - lib/redis/event_handler.ts | 1 - lib/utils/Commander.ts | 35 ++---- test/functional/drop_buffer_support.ts | 133 ---------------------- 8 files changed, 79 insertions(+), 198 deletions(-) delete mode 100644 test/functional/drop_buffer_support.ts diff --git a/README.md b/README.md index 2c4b777e..baff2941 100644 --- a/README.md +++ b/README.md @@ -19,20 +19,20 @@ ioredis is a robust, full-featured Redis client that is used in the world's biggest online commerce company [Alibaba](http://www.alibaba.com/) and many other awesome companies. 0. Full-featured. It supports [Cluster](http://redis.io/topics/cluster-tutorial), [Sentinel](http://redis.io/topics/sentinel), [Streams](https://redis.io/topics/streams-intro), [Pipelining](http://redis.io/topics/pipelining), and of course [Lua scripting](http://redis.io/commands/eval), [Redis Functions](https://redis.io/topics/functions-intro), [Pub/Sub](http://redis.io/topics/pubsub) (with the support of binary messages). -0. High performance 🚀. -0. Delightful API 😄. It works with Node callbacks and Native promises. -0. Transformation of command arguments and replies. -0. Transparent key prefixing. -0. Abstraction for Lua scripting, allowing you to [define custom commands](https://github.com/luin/ioredis#lua-scripting). -0. Supports [binary data](https://github.com/luin/ioredis#handle-binary-data). -0. Supports [TLS](https://github.com/luin/ioredis#tls-options) 🔒. -0. Supports offline queue and ready checking. -0. Supports ES6 types, such as `Map` and `Set`. -0. Supports GEO commands 📍. -0. Supports Redis ACL. -0. Sophisticated error handling strategy. -0. Supports NAT mapping. -0. Supports autopipelining +1. High performance 🚀. +2. Delightful API 😄. It works with Node callbacks and Native promises. +3. Transformation of command arguments and replies. +4. Transparent key prefixing. +5. Abstraction for Lua scripting, allowing you to [define custom commands](https://github.com/luin/ioredis#lua-scripting). +6. Supports [binary data](https://github.com/luin/ioredis#handle-binary-data). +7. Supports [TLS](https://github.com/luin/ioredis#tls-options) 🔒. +8. Supports offline queue and ready checking. +9. Supports ES6 types, such as `Map` and `Set`. +10. Supports GEO commands 📍. +11. Supports Redis ACL. +12. Sophisticated error handling strategy. +13. Supports NAT mapping. +14. Supports autopipelining # Versions @@ -105,35 +105,77 @@ $ npm install ioredis ```javascript const Redis = require("ioredis"); -const redis = new Redis(); // uses defaults unless given configuration object -// ioredis supports all Redis commands: -redis.set("foo", "bar"); // returns promise which resolves to string, "OK" +// First, you need to create a Redis instance. +// We are going to cover how to specify host, port, and other connection +// options soon. +const redis = new Redis(); -// the format is: redis[SOME_REDIS_COMMAND_IN_LOWERCASE](ARGUMENTS_ARE_JOINED_INTO_COMMAND_STRING) -// the js: ` redis.set("mykey", "Hello") ` is equivalent to the cli: ` redis> SET mykey "Hello" ` +// Invoke the SET command. This is equivalent to the cli `redis> SET name Bob`: +redis.set("name", "Bob"); // Returns a Promise -// ioredis supports the node.js callback style -redis.get("foo", function (err, result) { +// ioredis provides two kind of APIs, Node.js callback and Promise. +// 1. You can pass a callback as the last parameter. It will be called when +// we get a response from the Redis server: +redis.get("name", (err, value) => { if (err) { console.error(err); } else { - console.log(result); // Promise resolves to "bar" + console.log(value); // "Bob" + } +}); + +// 2. Additionally, every command method returns a Promise +// representing the server response: +redis.get("name").then( + (value) => { + console.log(value); + }, + (err) => { + console.error(err); } +); + +// + +// Every + +async function main() { + await redis.set("mykey", "Hello, World!"); + const value = await redis.get("mykey"); // value === "Hello, World!" +} + +main(); +``` + +// ioredis supports all Redis commands: +redis.set("foo", "bar"); // returns promise which resolves to string, "OK" + +// the format is: redis[REDIS_COMMAND_NAME_IN_LOWERCASE](ARGUMENTS_ARE_JOINED_INTO_COMMAND_STRING) +// the js: `redis.set("mykey", "Hello")` is equivalent to the cli: ` redis> SET mykey "Hello"` + +// ioredis supports the Node.js callback style +redis.get("foo", (err, result) => { +if (err) { +console.error(err); +} else { +console.log(result); // Promise resolves to "bar" +} }); // Or ioredis returns a promise if the last argument isn't a function -redis.get("foo").then(function (result) { - console.log(result); // Prints "bar" +redis.get("foo").then((result) => { +console.log(result); // Prints "bar" }); // Most responses are strings, or arrays of strings redis.zadd("sortedSet", 1, "one", 2, "dos", 4, "quatro", 3, "three"); -redis.zrange("sortedSet", 0, 2, "WITHSCORES").then((res) => console.log(res)); // Promise resolves to ["one", "1", "dos", "2", "three", "3"] as if the command was ` redis> ZRANGE sortedSet 0 2 WITHSCORES ` +redis.zrange("sortedSet", 0, 2, "WITHSCORES").then((res) => console.log(res)); // Promise resolves to ["one", "1", "dos", "2", "three", "3"] as if the command was `redis> ZRANGE sortedSet 0 2 WITHSCORES` // All arguments are passed directly to the redis server: redis.set("key", 100, "EX", 10); -``` + +```` See the `examples/` folder for more examples. @@ -155,7 +197,7 @@ new Redis({ password: "auth", db: 0, }); -``` +```` You can also specify connection options as a [`redis://` URL](http://www.iana.org/assignments/uri-schemes/prov/redis) or [`rediss://` URL](https://www.iana.org/assignments/uri-schemes/prov/rediss) when using [TLS encryption](#tls-options): diff --git a/lib/DataHandler.ts b/lib/DataHandler.ts index 5e8eca86..de22b0a2 100644 --- a/lib/DataHandler.ts +++ b/lib/DataHandler.ts @@ -29,14 +29,13 @@ interface DataHandledable extends EventEmitter { interface ParserOptions { stringNumbers: boolean; - dropBufferSupport: boolean; } export default class DataHandler { constructor(private redis: DataHandledable, parserOptions: ParserOptions) { const parser = new RedisParser({ stringNumbers: parserOptions.stringNumbers, - returnBuffers: !parserOptions.dropBufferSupport, + returnBuffers: true, returnError: (err: Error) => { this.returnError(err); }, diff --git a/lib/Pipeline.ts b/lib/Pipeline.ts index f1e53e24..d2a6c708 100644 --- a/lib/Pipeline.ts +++ b/lib/Pipeline.ts @@ -245,7 +245,6 @@ Pipeline.prototype.multi = function () { // @ts-expect-error const execBuffer = Pipeline.prototype.execBuffer; -const exec = Pipeline.prototype.exec; // @ts-expect-error Pipeline.prototype.execBuffer = deprecate(function () { if (this._transactions > 0) { @@ -278,10 +277,7 @@ Pipeline.prototype.exec = function (callback: Callback): Promise> { if (this._transactions > 0) { this._transactions -= 1; - return (this.options.dropBufferSupport ? exec : execBuffer).apply( - this, - arguments - ); + return execBuffer.apply(this, arguments); } if (!this.nodeifiedPromise) { this.nodeifiedPromise = true; diff --git a/lib/connectors/SentinelConnector/index.ts b/lib/connectors/SentinelConnector/index.ts index 9137145a..539b9e37 100644 --- a/lib/connectors/SentinelConnector/index.ts +++ b/lib/connectors/SentinelConnector/index.ts @@ -296,7 +296,6 @@ export default class SentinelConnector extends AbstractConnector { enableReadyCheck: false, connectTimeout: this.options.connectTimeout, commandTimeout: this.options.sentinelCommandTimeout, - dropBufferSupport: true, ...options, }); // @ts-expect-error diff --git a/lib/redis/RedisOptions.ts b/lib/redis/RedisOptions.ts index 90c9fc26..782d5526 100644 --- a/lib/redis/RedisOptions.ts +++ b/lib/redis/RedisOptions.ts @@ -15,7 +15,6 @@ interface CommonRedisOptions extends CommanderOptions { username?: string; password?: string; db?: number; - dropBufferSupport?: boolean; autoResubscribe?: boolean; autoResendUnfulfilledCommands?: boolean; reconnectOnError?: ReconnectOnError; @@ -76,7 +75,6 @@ export const DEFAULT_REDIS_OPTIONS: RedisOptions = { password: null, db: 0, // Others - dropBufferSupport: false, enableOfflineQueue: true, enableReadyCheck: true, autoResubscribe: true, diff --git a/lib/redis/event_handler.ts b/lib/redis/event_handler.ts index 4588caa2..8b3b57d0 100644 --- a/lib/redis/event_handler.ts +++ b/lib/redis/event_handler.ts @@ -72,7 +72,6 @@ export function connectHandler(self) { */ new DataHandler(self, { stringNumbers: self.options.stringNumbers, - dropBufferSupport: self.options.dropBufferSupport, }); if (self.options.enableReadyCheck) { diff --git a/lib/utils/Commander.ts b/lib/utils/Commander.ts index e6699ca4..c9ed21c9 100644 --- a/lib/utils/Commander.ts +++ b/lib/utils/Commander.ts @@ -1,5 +1,4 @@ import { list } from "@ioredis/commands"; -import asCallback from "standard-as-callback"; import { executeWithAutoPipelining, shouldUseAutoPipelining, @@ -14,11 +13,6 @@ export interface CommanderOptions { showFriendlyErrorStack?: boolean; } -const DROP_BUFFER_SUPPORT_ERROR = - "*Buffer methods are not available " + - 'because "dropBufferSupport" option is enabled.' + - "Refer to https://github.com/luin/ioredis/wiki/Improve-Performance for more details."; - // eslint-disable-next-line @typescript-eslint/no-unused-vars class Commander { options: CommanderOptions = {}; @@ -154,13 +148,6 @@ function generateFunction( replyEncoding: _encoding, }; - if (this.options.dropBufferSupport && !_encoding) { - return asCallback( - Promise.reject(new Error(DROP_BUFFER_SUPPORT_ERROR)), - callback as Callback | undefined - ); - } - // No auto pipeline, use regular command sending if (!shouldUseAutoPipelining(this, functionName, commandName)) { return this.sendCommand( @@ -185,24 +172,18 @@ function generateScriptingFunction( functionName: string, commandName: string, script: Script, - encoding: unknown + encoding: BufferEncoding | null ) { - return function (...args) { + return function (...args: any[]) { const callback = typeof args[args.length - 1] === "function" ? args.pop() : undefined; - let options; - if (this.options.dropBufferSupport) { - if (!encoding) { - return asCallback( - Promise.reject(new Error(DROP_BUFFER_SUPPORT_ERROR)), - callback - ); - } - options = { replyEncoding: null }; - } else { - options = { replyEncoding: encoding }; - } + const options: { + replyEncoding: BufferEncoding | null; + errorStack?: Error; + } = { + replyEncoding: encoding, + }; if (this.options.showFriendlyErrorStack) { options.errorStack = new Error(); diff --git a/test/functional/drop_buffer_support.ts b/test/functional/drop_buffer_support.ts deleted file mode 100644 index b47ea1b1..00000000 --- a/test/functional/drop_buffer_support.ts +++ /dev/null @@ -1,133 +0,0 @@ -import Redis from "../../lib/Redis"; -import { expect } from "chai"; - -describe("dropBufferSupport", () => { - it("should be disabled by default", () => { - const redis = new Redis({ lazyConnect: true }); - expect(redis.options).to.have.property("dropBufferSupport", false); - }); - - it("should return strings correctly", (done) => { - const redis = new Redis({ dropBufferSupport: false }); - redis.set("foo", Buffer.from("bar"), function (err, res) { - expect(err).to.eql(null); - expect(res).to.eql("OK"); - redis.get("foo", function (err, res) { - expect(err).to.eql(null); - expect(res).to.eql("bar"); - redis.disconnect(); - done(); - }); - }); - }); - - context("enabled", () => { - it("should reject the buffer commands", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - redis.getBuffer("foo", function (err) { - expect(err.message).to.match(/Buffer methods are not available/); - - redis.callBuffer("get", "foo", function (err) { - expect(err.message).to.match(/Buffer methods are not available/); - redis.disconnect(); - done(); - }); - }); - }); - - it("should reject the custom buffer commands", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - redis.defineCommand("geteval", { - numberOfKeys: 0, - lua: 'return "string"', - }); - redis.getevalBuffer(function (err) { - expect(err.message).to.match(/Buffer methods are not available/); - redis.disconnect(); - done(); - }); - }); - - it("should return strings correctly", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - redis.set("foo", Buffer.from("bar"), function (err, res) { - expect(err).to.eql(null); - expect(res).to.eql("OK"); - redis.get("foo", function (err, res) { - expect(err).to.eql(null); - expect(res).to.eql("bar"); - redis.disconnect(); - done(); - }); - }); - }); - - it("should return strings for custom commands", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - redis.defineCommand("geteval", { - numberOfKeys: 0, - lua: 'return "string"', - }); - redis.geteval(function (err, res) { - expect(err).to.eql(null); - expect(res).to.eql("string"); - redis.disconnect(); - done(); - }); - }); - - it("should work with pipeline", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - const pipeline = redis.pipeline(); - pipeline.set("foo", "bar"); - pipeline.get(Buffer.from("foo")); - pipeline.exec(function (err, res) { - expect(err).to.eql(null); - expect(res[0][1]).to.eql("OK"); - expect(res[1][1]).to.eql("bar"); - redis.disconnect(); - done(); - }); - }); - - it("should work with transaction", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - redis - .multi() - .set("foo", "bar") - .get("foo") - .exec(function (err, res) { - expect(err).to.eql(null); - expect(res[0][1]).to.eql("OK"); - expect(res[1][1]).to.eql("bar"); - redis.disconnect(); - done(); - }); - }); - - it("should fail early with Buffer transaction", (done) => { - const redis = new Redis({ dropBufferSupport: true }); - redis - .multi() - .set("foo", "bar") - .getBuffer(Buffer.from("foo"), function (err) { - expect(err.message).to.match(/Buffer methods are not available/); - redis.disconnect(); - done(); - }); - }); - - it("should work with internal select command", (done) => { - const redis = new Redis({ dropBufferSupport: true, db: 1 }); - const check = new Redis({ db: 1 }); - redis.set("foo", "bar", () => { - check.get("foo", function (err, res) { - expect(res).to.eql("bar"); - redis.disconnect(); - check.disconnect(); - done(); - }); - }); - }); - }); -});