Skip to content

Commands' reply schema #9845

@guybe7

Description

@guybe7

Background

In #9656 we add a lot of information about Redis commands, but we are missing information about the replies

Motivation

  1. Documentation. This is the primary goal.
  2. We would like, based on the output of COMMAND, to be able to generate client code in typed languages. In order to do that, we need Redis to tell us, in detail, how each reply looks like.
  3. We would like to build a fuzzer that verifies the reply structure.

Schema

The idea is to supply some sort of schema for each reply of each command.
The schema will describe the conceptual structure of the reply (for generated clients), and, in case it differs from the actual (wire) reply, it will be mentioned.
null-reply is a special case since RESP2 has two types of null-reply (null-bulk-string and null-array), so for every command that may return null, we have to mention the wire-reply.

Example for ZSCORE:

{
  "oneOf": [
    {
      "description": "Score of the member",
      "type": "number",
      "wire": {
        "resp2": "string",
        "resp3": "double"
      }
    },
    {
      "description": "Key or member does not exist",
      "type": "null",
      "wire": {
        "resp2": "null-bulk-string",
        "resp3": "null"
      }
    }
  ]
}

Example for HGETALL:

{
  "type": "object",
  "additionalProperties": {
    "type": "string"
  }
}

Note that here we don't really have to mention the wire-reply (the type is "object" which is a map, which is the return type of the command in RESP3. the only way to represent a map in RESP2 is an array, so determining the wire-reply is implicit in that case)

Example for ZRANGE:

{
  "oneOf": [
    {
      "description": "A list of member elements",
      "type": "array",
      "resp": 2,
      "items": {
        "type": "string"
      },
      "uniqueItems": true,
      "minItems": 1
    },
    {
      "type": "array",
      "description": "Members and their scores",
      "items": {
        "type": "array",
        "items": [
          {
            "description": "Member",
            "type": "string"
          },
          {
            "description": "Score",
            "type": "number"
          }
        ],
        "additionalItems": false,
        "minItems": 1
      },
      "uniqueItems": true,
      "minItems": 1
    }
  ]
}

Here, both RESP2 and RESP3 will return an array, so no need to mention the wire-reply type, but RESP3 will return exactly the array described above, while RESP2 will return a flat array of member-score. The client needs to how to convert the actual wire-reply to the reply described in the schema (see "Conversion rules" below)

Conversion rules

In addition, we will have to supply general conversion rules in this structure:
If the reply structure is THIS but instead you got THAT, here's how to convert: ...

Examples (relevant for RESP2 only):
If the reply structure is an "object" but you got an "array" the way to convert is: {arr[i]: arr[i+1]} for each even i.
If the reply structure is a "number" but you got a "string" the way to convert is: cast the string into a double

There are more complex situations that require some thinking (For example ZRANGE WITHSCORES should return an array of (member, score) tuples, but in RESP2 it's a flat array)

Thoughts

  1. the example for ZRANGE doesn't cover the case of two identical members with different scores (e.g. [["m1",6],["m1",7]]) because uniqueItems compares (member,score) tuples and not just the member name. is there a way to solve this?
  2. the reply schema is gonna be based on a JSON scheme. the type of a string is "string" but even in RESP3 we have 2 strings: simple-string and bulk-string. do we want to reflect that somehow in the reply-schema?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions