resp3

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 10, 2024 License: Apache-2.0 Imports: 9 Imported by: 0

README

resp3

License GoDoc travis Go Report Card coveralls

A redis RESP3 protocol library, written in Go.

Value represents a redis command or an redis response, which supports all RESP3 types:

Types equivalent to RESP version 2
  • Array: an ordered collection of N other types
  • Blob string: binary safe strings
  • Simple string: a space efficient non binary safe string
  • Simple error: a space efficient non binary safe error code and message
  • Number: an integer in the signed 64 bit range
Types introduced by RESP3
  • Null: a single null value replacing RESP v2 *-1 and $-1 null values.
  • Double: a floating point number
  • Boolean: true or false
  • Blob error: binary safe error code and message.
  • Verbatim string: a binary safe string that should be displayed to humans without any escaping or filtering. For instance the output of LATENCY DOCTOR in Redis.
  • Map: an ordered collection of key-value pairs. Keys and values can be any other RESP3 type.
  • Set: an unordered collection of N other types.
  • Attribute: Like the Map type, but the client should keep reading the reply ignoring the attribute type, and return it to the client as additional information.
  • Push: Out of band data. The format is like the Array type, but the client should just check the first string element, stating the type of the out of band data, a call a callback if there is one registered for this specific type of push information. Push types are not related to replies, since they are information that the server may push at any time in the connection, so the client should keep reading if it is reading the reply of a command.
  • Hello: Like the Map type, but is sent only when the connection between the client and the server is established, in order to welcome the client with different information like the name of the server, its version, and so forth.
  • Big number: a large number non representable by the Number type

Reader is used to read a Value object from the connection. It can be used by both redis clients and redis servers.

Examples

Client Cache (Tracking)

you can use it to test the client cache feature in Redis 6.0.

func TestReader_IT_Tracking(t *testing.T) {
	conn, err := net.DialTimeout("tcp", "127.0.0.1:6379", 5*time.Second)
	if err != nil {
		t.Logf("can't found one of redis 6.0 server")
		return
	}
	defer conn.Close()

	w := NewWriter(conn)
	r := NewReader(conn)

	w.WriteCommand("HELLO", "3")
	helloResp, _, err := r.ReadValue()
	if err != nil {
		t.Fatalf("failed to send a HELLO 3")
	}
	if helloResp.KV.Size() == 0 {
		t.Fatalf("expect some info but got %+v", helloResp)
	}
	t.Logf("hello response: %c, %v", helloResp.Type, helloResp.SmartResult())

	w.WriteCommand("CLIENT", "TRACKING", "on")
	resp, _, err := r.ReadValue()
	if err != nil {
		t.Fatalf("failed to TRACKING: %v", err)
	}
	t.Logf("TRACKING result: %c, %+v", resp.Type, resp.SmartResult())

	w.WriteCommand("GET", "a")
	resp, _, err = r.ReadValue()
	if err != nil {
		t.Fatalf("failed to GET: %v", err)
	}
	t.Logf("GET result: %c, %+v", resp.Type, resp.SmartResult())

	go func() {
		conn, err := net.DialTimeout("tcp", "127.0.0.1:9999", 5*time.Second)
		if err != nil {
			t.Logf("can't found one of redis 6.0 server")
			return
		}
		defer conn.Close()
		w := NewWriter(conn)
		r := NewReader(conn)

		for i := 0; i < 10; i++ {
			//PUBLISH
			w.WriteCommand("set", "a", strconv.Itoa(i))
			resp, _, err = r.ReadValue()
			if err != nil {
				t.Fatalf("failed to set: %v", err)
			}
			t.Logf("set result: %c, %+v", resp.Type, resp.SmartResult())
			time.Sleep(200 * time.Millisecond)
		}

	}()

	for i := 0; i < 10; i++ {
		resp, _, err = r.ReadValue()
		if err != nil {
			t.Fatalf("failed to receive a message: %v", err)
		}
		if resp.Type == TypePush && len(resp.Elems) >= 2 && resp.Elems[0].SmartResult().(string) == "invalidate" {
			t.Logf("received TRACKING result: %c, %+v", resp.Type, resp.SmartResult())

			// refresh cache "a"
			w.WriteCommand("GET", "a")
			resp, _, err = r.ReadValue()
		}
	}
}

Documentation

Overview

Package resp3 implements redis RESP3 protocol, which is used from redis 6.0.

RESP (REdis Serialization Protocol) is the protocol used in the Redis database, however the protocol is designed to be used by other projects. With the version 3 of the protocol, currently a work in progress design, the protocol aims to become even more generally useful to other systems that want to implement a protocol which is simple, efficient, and with a very large landscape of client libraries implementations. That means you can use this library to access other RESP3 projects.

This library contains three important components: Value, Reader and Writer.

Value represents a redis command or a redis response. It is a common struct for all RESP3 types.

Reader can parse redis responses from redis servers or commands from redis clients. You can use it to implement redis 6.0 clients, no need to pay attention to underlying parsing. Those new features of redis 6.0 can be implemented based on it.

Writer is redis writer. You can use it to send commands to redis servers.

RESP3 spec can be found at https://github.com/antirez/RESP3.

A redis client based on it is just as the below:

conn, err := net.DialTimeout("tcp", "127.0.0.1:6379", 5*time.Second)
if err != nil {
	t.Logf("can't found one of redis 6.0 server")
	return
}
defer conn.Close()

w := NewWriter(conn)
r := NewReader(conn)

// send protocol version 3, get a map result
w.WriteCommand("HELLO", "3")
resp, _, _ := r.ReadValue()
log.Printf("%v", resp.SmartResult())

// set
w.WriteCommand("SET", "A", "123")
resp, _, _ := r.ReadValue()
log.Printf("%v", resp.SmartResult())

// get
w.WriteCommand("GET", "A")
resp, _, _ = r.ReadValue()
log.Printf("%v", resp.SmartResult())

Index

Constants

View Source
const (
	TypeBlobString     = '$' // $<length>\r\n<bytes>\r\n
	TypeSimpleString   = '+' // +<string>\r\n
	TypeSimpleError    = '-' // -<string>\r\n
	TypeNumber         = ':' // :<number>\r\n
	TypeNull           = '_' // _\r\n
	TypeDouble         = ',' // ,<floating-point-number>\r\n
	TypeBoolean        = '#' // #t\r\n or #f\r\n
	TypeBlobError      = '!' // !<length>\r\n<bytes>\r\n
	TypeVerbatimString = '=' // =<length>\r\n<format(3 bytes):><bytes>\r\n
	TypeBigNumber      = '(' // (<big number>\n

	TypeArray     = '*' // *<elements number>\r\n... numelements other types ...
	TypeMap       = '%' // %<elements number>\r\n... numelements key/value pair of other types ...
	TypeSet       = '~' // ~<elements number>\r\n... numelements other types ...
	TypeAttribute = '|' // |~<elements number>\r\n... numelements map type ...
	TypePush      = '>' // ><elements number>\r\n<first item is String>\r\n... numelements-1 other types ...

	//special type
	TypeStream = "$EOF:" // $EOF:<40 bytes marker><CR><LF>... any number of bytes of data here not containing the marker ...<40 bytes marker>
)

resp3 type char

View Source
const CRLF = "\r\n"
View Source
const TRACKING_TABLE_SIZE = 1 << 24

Variables

View Source
var (
	ErrInvalidSyntax      = errors.New("resp: invalid syntax")
	ErrStreamingUnsupport = errors.New("resp: unsupported streaming")
	ErrUnknown            = errors.New("resp: unknown")
)

Errors

View Source
var CRLFByte = []byte(CRLF)
View Source
var StreamMarkerPrefix = []byte("$EOF:")

Functions

func Hash

func Hash(data []byte) uint32

Hash with crc64.

Types

type Reader

type Reader struct {
	*bufio.Reader
}

Reader is reader to parse responses/requests from the underlying reader.

func NewReader

func NewReader(reader io.Reader) *Reader

NewReader returns a RESP3 reader.

func NewReaderSize

func NewReaderSize(reader io.Reader, size int) *Reader

NewReaderSize returns a new Reader whose buffer has at least the specified size.

func (*Reader) ReadRaw

func (r *Reader) ReadRaw() ([]byte, error)

ReadRaw parses a RESP3 raw byte slice.

func (*Reader) ReadValue

func (r *Reader) ReadValue() (*Value, []byte, error)

ReadValue parses a RESP3 value.

type Value

type Value struct {
	Type         byte
	Str          string
	StrFmt       string
	Err          string
	Integer      int64
	Boolean      bool
	Double       float64
	BigInt       *big.Int
	Elems        []*Value           // for array & set
	KV           *linkedhashmap.Map //TODO sorted map, for map & attr
	Attrs        *linkedhashmap.Map
	StreamMarker string
}

Value is a common struct for all RESP3 type. There is no exact field for NULL type because the type field is enough. It is not used for Stream type.

func FromString

func FromString(data string) (*Value, error)

FromString convert a string into a Value.

func NewArrayValue

func NewArrayValue(elems []*Value) *Value

NewArrayValue make a value with type Array

func NewAttributeValue

func NewAttributeValue(attrs *linkedhashmap.Map) *Value

NewAttributeValue make a value with type Attribute

func NewBigNumberValue

func NewBigNumberValue(bigInt *big.Int) *Value

NewBigNumberValue make a value with type BigNumber

func NewBlobErrorValue

func NewBlobErrorValue(err error) *Value

NewBlobErrorValue make a value with type BlobError

func NewBlobStringValue

func NewBlobStringValue(s string) *Value

NewBlobStringValue make a value with type BlobString

func NewBooleanValue

func NewBooleanValue(boolean bool) *Value

NewBooleanValue make a value with type Boolean

func NewDoubleValue

func NewDoubleValue(double float64) *Value

NewDoubleValue make a value with type Double

func NewMapValue

func NewMapValue(kv *linkedhashmap.Map) *Value

NewMapValue make a value with type Map

func NewNullValue

func NewNullValue() *Value

NewNullValue make a value with type Null

func NewNumberValue

func NewNumberValue(integer int64) *Value

NewNumberValue make a value with type Number

func NewPushValue

func NewPushValue(elems []*Value) *Value

NewPushValue make a value with type Push

func NewSetValue

func NewSetValue(elems []*Value) *Value

NewSetValue make a value with type Set

func NewSimpleErrorValue

func NewSimpleErrorValue(err error) *Value

NewSimpleErrorValue make a value with type SimpleError

func NewSimpleStringValue

func NewSimpleStringValue(s string) *Value

NewSimpleStringValue make a value with type SimpleString

func NewVerbatimStringValue

func NewVerbatimStringValue(str string, fmt string) *Value

NewVerbatimStringValue make a value with type VerbatimString

func (*Value) SmartResult

func (r *Value) SmartResult() interface{}

SmartResult converts itself to a real object. Attributes are dropped. simple objects are converted their Go types. String -> go string Interger -> go int64 Double -> go float64 Boolean -> go bool Err -> go string BigInt -> big.Int Array -> go array Map --> github.com/emirpasic/gods/maps/linkedhashmap.Map Set -> go array Push -> go array NULL -> nil

func (*Value) ToRESP3String

func (r *Value) ToRESP3String() string

ToRESP3String converts this value to redis RESP3 string.

type Writer

type Writer struct {
	*bufio.Writer
}

Writer is a redis client writer. he RESP3 protocol can be used asymmetrically, as it is in Redis: only a subset can be sent by the client to the server, while the server can return the full set of types available. This is due to the fact that RESP is designed to send non structured commands like SET mykey somevalue or SADD myset a b c d. Such commands can be represented as arrays, where each argument is an array element, so this is the only type the client needs to send to a server.

func NewWriter

func NewWriter(writer io.Writer) *Writer

NewWriter returns a redis client writer.

func (*Writer) WriteByteCommand

func (w *Writer) WriteByteCommand(args ...[]byte) (err error)

WriteByteCommand writes a redis command in bytes.

func (*Writer) WriteCommand

func (w *Writer) WriteCommand(args ...string) (err error)

WriteCommand writes a redis command.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL