Skip to content

Commit

Permalink
add: add bound cases for resp reader
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshi-099 committed Jun 24, 2024
1 parent 6f6bc9f commit fb15f0a
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 60 deletions.
4 changes: 2 additions & 2 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ func TestCommand(t *testing.T) {
assert := assert.New(t)

go startup()
time.Sleep(time.Second / 5)
time.Sleep(time.Second / 3)

// wait for client starup
rdb := redis.NewClient(&redis.Options{
Addr: ":20082",
})
time.Sleep(time.Second / 5)
time.Sleep(time.Second / 3)

t.Run("ping", func(t *testing.T) {
res, _ := rdb.Ping(ctx).Result()
Expand Down
27 changes: 5 additions & 22 deletions internal/dict/dict.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ type Dict struct {
}

func New(options Options) *Dict {
if err := validateOptions(options); err != nil {
panic(err)
}
dict := &Dict{
mask: options.ShardCount - 1,
shards: make([]*shard, options.ShardCount),
Expand Down Expand Up @@ -133,12 +130,10 @@ func (dict *Dict) EvictExpired() {

// Stats represents the runtime statistics of Dict.
type Stats struct {
Len int
Alloc uint64
Unused uint64
Migrates uint64
Evictions uint64
Probes uint64
Len int
Alloc uint64
Unused uint64
Migrates uint64
}

// GetStats returns the current runtime statistics of Dict.
Expand All @@ -148,8 +143,6 @@ func (c *Dict) GetStats() (stats Stats) {
stats.Alloc += uint64(len(shard.data))
stats.Unused += uint64(shard.unused)
stats.Migrates += uint64(shard.migrations)
stats.Evictions += shard.evictions
stats.Probes += shard.probes
}
return
}
Expand All @@ -159,20 +152,13 @@ func (s Stats) UnusedRate() float64 {
return float64(s.Unused) / float64(s.Alloc) * 100
}

// EvictionRate calculates the percentage of evictions relative to probes.
func (s Stats) EvictionRate() float64 {
return float64(s.Evictions) / float64(s.Probes) * 100
}

// shard is the data container for Dict.
type shard struct {
options *Options
index *swiss.Map[string, Idx]
data []byte
unused uint32
migrations uint32
evictions uint64
probes uint64
}

func (s *shard) appendEntry(val []byte, ts int64) Idx {
Expand Down Expand Up @@ -201,13 +187,10 @@ func (s *shard) evictExpired() {

// probing
s.index.All(func(key string, idx Idx) bool {
s.probes++
failed++
if idx.expiredWith(nanosec) {
s.removeEntry(key, idx)
s.evictions++
failed = 0
} else {
failed++
}
return failed <= maxFailed
})
Expand Down
3 changes: 0 additions & 3 deletions internal/dict/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ func benchmark(options dict.Options) {
formatSize(stat.Alloc),
stat.UnusedRate(),
)
fmt.Printf("[Evict] probe: %vw / %vw (%.1f%%) | mgr: %d\n",
stat.Evictions/1e5, stat.Probes/1e5, stat.EvictionRate(),
stat.Migrates)

// mem stats
runtime.ReadMemStats(&memStats)
Expand Down
8 changes: 0 additions & 8 deletions internal/dict/utils.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package dict

import (
"errors"
"math/bits"
"unsafe"
)
Expand Down Expand Up @@ -48,10 +47,3 @@ var DefaultOptions = Options{
BufferSize: 32 * KB,
MigrateRatio: 0.4,
}

func validateOptions(options Options) error {
if options.ShardCount == 0 {
return errors.New("cache/options: invalid shard count")
}
return nil
}
3 changes: 0 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ func main() {
if err != nil {
log.Fatal().Msgf("load config error: %v", err)
}

log.Debug().Bool("appendonly", config.AppendOnly).Int("port", config.Port).Msg("read config file")

if err = initServer(config); err != nil {
log.Fatal().Msgf("init server error: %v", err)
}
Expand Down
29 changes: 9 additions & 20 deletions resp.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ func NewReader(input []byte) *RESPReader {

// cutByCRLF splits the buffer by the first occurrence of CRLF.
func cutByCRLF(buf []byte) (before, after []byte, found bool) {
if len(buf) <= 2 {
n := len(buf)
if n <= 2 {
return
}
for i, b := range buf {
for i, b := range buf[:n-1] {
if b == '\r' {
if buf[i+1] == '\n' {
return buf[:i], buf[i+2:], true
Expand Down Expand Up @@ -160,32 +161,20 @@ func (w *RESPWriter) WriteNull() {
}

// Reset resets the internal buffer.
func (w *RESPWriter) Reset() {
w.b.Reset()
}
func (w *RESPWriter) Reset() { w.b.Reset() }

// RESP represents the RESP (Redis Serialization Protocol) message in byte slice format.
type RESP []byte

func (r RESP) ToString() string {
return string(r)
}
func (r RESP) ToString() string { return string(r) }

func (r RESP) ToStringUnsafe() string {
return b2s(r)
}
func (r RESP) ToStringUnsafe() string { return b2s(r) }

func (r RESP) ToInt() (int, error) {
return strconv.Atoi(b2s(r))
}
func (r RESP) ToInt() (int, error) { return strconv.Atoi(b2s(r)) }

func (r RESP) ToBytes() []byte {
return r
}
func (r RESP) ToBytes() []byte { return r }

func (r RESP) Clone() []byte {
return slices.Clone(r)
}
func (r RESP) Clone() []byte { return slices.Clone(r) }

func b2s(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
Expand Down
14 changes: 12 additions & 2 deletions resp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func TestWriter(t *testing.T) {
assert.Equal(writer.b.String(), ":5\r\n")
writer.Reset()
})
}

func TestReader(t *testing.T) {
assert := assert.New(t)

t.Run("error-reader", func(t *testing.T) {
// read nil
Expand Down Expand Up @@ -65,17 +69,23 @@ func TestWriter(t *testing.T) {
// error cases
_, _, ok = cutByCRLF([]byte("A"))
assert.False(ok)

_, _, ok = cutByCRLF([]byte("ABC"))
assert.False(ok)

_, _, ok = cutByCRLF([]byte("1234\r"))
assert.False(ok)
})

t.Run("readCommandBulk", func(t *testing.T) {
t.Run("command-bulk", func(t *testing.T) {
args, err := NewReader([]byte("*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n")).ReadNextCommand(nil)
assert.Equal(args[0].ToString(), "SET")
assert.Equal(args[1].ToString(), "foo")
assert.Equal(args[2].ToString(), "bar")
assert.Nil(err)
})

t.Run("readCommandInline", func(t *testing.T) {
t.Run("command-inline", func(t *testing.T) {
args, err := NewReader([]byte("PING\r\n")).ReadNextCommand(nil)
assert.Equal(args[0].ToString(), "PING")
assert.Nil(err)
Expand Down

0 comments on commit fb15f0a

Please sign in to comment.