Skip to content

Commit c298774

Browse files
authored
Support for latest TDigest [RedisBloom 2.4.x] (#48)
1 parent 05f9cca commit c298774

File tree

6 files changed

+137
-38
lines changed

6 files changed

+137
-38
lines changed

.github/workflows/integration.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Integration
2+
3+
on:
4+
push:
5+
paths-ignore:
6+
- 'docs/**'
7+
- '**/*.rst'
8+
- '**/*.md'
9+
branches:
10+
- master
11+
- main
12+
- '[0-9].[0-9]'
13+
pull_request:
14+
branches:
15+
- master
16+
- main
17+
- '[0-9].[0-9]'
18+
schedule:
19+
- cron: '0 1 * * *'
20+
21+
jobs:
22+
23+
lint:
24+
name: Code linters
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v3
28+
- run: |
29+
make checkfmt
30+
make lint
31+
32+
integration:
33+
services:
34+
image: redislabs/rebloom:edge
35+
port:
36+
- 6379:6379
37+
name: Build and test
38+
runs-on: ubuntu-latest
39+
steps:
40+
- uses: actions/setup-go@v3
41+
with:
42+
go-version: 1.18.x
43+
- uses: actions/checkout@v3
44+
- run: |
45+
make get
46+
make coverage
47+
- name: Upload coverage
48+
uses: codecov/codecov-action@v3

client.go

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package redis_bloom_go
33
import (
44
"errors"
55
"fmt"
6-
"github.com/gomodule/redigo/redis"
76
"strconv"
87
"strings"
8+
9+
"github.com/gomodule/redigo/redis"
910
)
1011

1112
// TODO: refactor this hard limit and revise client locking
@@ -24,8 +25,8 @@ type TDigestInfo struct {
2425
capacity int64
2526
mergedNodes int64
2627
unmergedNodes int64
27-
mergedWeight float64
28-
unmergedWeight float64
28+
mergedWeight int64
29+
unmergedWeight int64
2930
totalCompressions int64
3031
}
3132

@@ -50,12 +51,12 @@ func (info *TDigestInfo) UnmergedNodes() int64 {
5051
}
5152

5253
// MergedWeight - returns the merged weight of TDigestInfo instance
53-
func (info *TDigestInfo) MergedWeight() float64 {
54+
func (info *TDigestInfo) MergedWeight() int64 {
5455
return info.mergedWeight
5556
}
5657

5758
// UnmergedWeight - returns the unmerged weight of TDigestInfo instance
58-
func (info *TDigestInfo) UnmergedWeight() float64 {
59+
func (info *TDigestInfo) UnmergedWeight() int64 {
5960
return info.unmergedWeight
6061
}
6162

@@ -499,7 +500,7 @@ func (client *Client) CfInfo(key string) (map[string]int64, error) {
499500
func (client *Client) TdCreate(key string, compression int64) (string, error) {
500501
conn := client.Pool.Get()
501502
defer conn.Close()
502-
return redis.String(conn.Do("TDIGEST.CREATE", key, compression))
503+
return redis.String(conn.Do("TDIGEST.CREATE", key, "COMPRESSION", compression))
503504
}
504505

505506
// TdReset - Reset the sketch to zero - empty out the sketch and re-initialize it
@@ -521,11 +522,48 @@ func (client *Client) TdAdd(key string, samples map[float64]float64) (string, er
521522
return redis.String(reply, err)
522523
}
523524

524-
// TdMerge - Merges all of the values from 'from' to 'this' sketch
525-
func (client *Client) TdMerge(toKey string, fromKey string) (string, error) {
525+
// tdMerge - The internal representation of TdMerge. All underlying functions call this one,
526+
// returning its results. It allows us to maintain interfaces.
527+
// see https://redis.io/commands/tdigest.merge/
528+
//
529+
// The default values for compression is 100
530+
func (client *Client) tdMerge(toKey string, compression int64, override bool, numKeys int64, fromKey ...string) (string, error) {
531+
if numKeys < 1 {
532+
return "", errors.New("a minimum of one key must be merged")
533+
}
534+
526535
conn := client.Pool.Get()
527536
defer conn.Close()
528-
return redis.String(conn.Do("TDIGEST.MERGE", toKey, fromKey))
537+
overidable := ""
538+
if override {
539+
overidable = "1"
540+
}
541+
return redis.String(conn.Do("TDIGEST.MERGE", toKey,
542+
strconv.FormatInt(numKeys, 10),
543+
strings.Join(fromKey, " "),
544+
"COMPRESSION", compression,
545+
overidable))
546+
}
547+
548+
// TdMerge - Merges all of the values from 'from' to 'this' sketch
549+
func (client *Client) TdMerge(toKey string, numKeys int64, fromKey ...string) (string, error) {
550+
return client.tdMerge(toKey, 100, false, numKeys, fromKey...)
551+
}
552+
553+
// TdMergeWithCompression - Merges all of the values from 'from' to 'this' sketch with specified compression
554+
func (client *Client) TdMergeWithCompression(toKey string, compression int64, numKeys int64, fromKey ...string) (string, error) {
555+
return client.tdMerge(toKey, compression, false, numKeys, fromKey...)
556+
}
557+
558+
// TdMergeWithOverride - Merges all of the values from 'from' to 'this' sketch overriding the destination key if it exists
559+
func (client *Client) TdMergeWithOverride(toKey string, override bool, numKeys int64, fromKey ...string) (string, error) {
560+
return client.tdMerge(toKey, 100, true, numKeys, fromKey...)
561+
}
562+
563+
// TdMergeWithCompressionAndOverride - Merges all of the values from 'from' to 'this' sketch with specified compression
564+
// and overriding the destination key if it exists
565+
func (client *Client) TdMergeWithCompressionAndOverride(toKey string, compression int64, numKeys int64, fromKey ...string) (string, error) {
566+
return client.tdMerge(toKey, compression, true, numKeys, fromKey...)
529567
}
530568

531569
// TdMin - Get minimum value from the sketch. Will return DBL_MAX if the sketch is empty
@@ -544,17 +582,22 @@ func (client *Client) TdMax(key string) (float64, error) {
544582

545583
// TdQuantile - Returns an estimate of the cutoff such that a specified fraction of the data added
546584
// to this TDigest would be less than or equal to the cutoff
547-
func (client *Client) TdQuantile(key string, quantile float64) (float64, error) {
585+
func (client *Client) TdQuantile(key string, quantile float64) ([]float64, error) {
548586
conn := client.Pool.Get()
549587
defer conn.Close()
550-
return redis.Float64(conn.Do("TDIGEST.QUANTILE", key, quantile))
588+
return redis.Float64s(conn.Do("TDIGEST.QUANTILE", key, quantile))
551589
}
552590

553-
// TdCdf - Returns the fraction of all points added which are <= value
554-
func (client *Client) TdCdf(key string, value float64) (float64, error) {
591+
// TdCdf - Returns the list of fractions of all points added which are <= values
592+
func (client *Client) TdCdf(key string, values ...float64) ([]float64, error) {
555593
conn := client.Pool.Get()
556594
defer conn.Close()
557-
return redis.Float64(conn.Do("TDIGEST.CDF", key, value))
595+
596+
args := make([]string, len(values))
597+
for idx, obj := range values {
598+
args[idx] = strconv.FormatFloat(obj, 'f', -1, 64)
599+
}
600+
return redis.Float64s(conn.Do("TDIGEST.CDF", key, strings.Join(args, " ")))
558601
}
559602

560603
// TdInfo - Returns compression, capacity, total merged and unmerged nodes, the total
@@ -590,6 +633,9 @@ func ParseTDigestInfo(result interface{}, err error) (info TDigestInfo, outErr e
590633
var key string
591634
for i := 0; i < len(values); i += 2 {
592635
key, outErr = redis.String(values[i], nil)
636+
if outErr != nil {
637+
return TDigestInfo{}, outErr
638+
}
593639
switch key {
594640
case "Compression":
595641
info.compression, outErr = redis.Int64(values[i+1], nil)
@@ -600,9 +646,9 @@ func ParseTDigestInfo(result interface{}, err error) (info TDigestInfo, outErr e
600646
case "Unmerged nodes":
601647
info.unmergedNodes, outErr = redis.Int64(values[i+1], nil)
602648
case "Merged weight":
603-
info.mergedWeight, outErr = redis.Float64(values[i+1], nil)
649+
info.mergedWeight, outErr = redis.Int64(values[i+1], nil)
604650
case "Unmerged weight":
605-
info.unmergedWeight, outErr = redis.Float64(values[i+1], nil)
651+
info.unmergedWeight, outErr = redis.Int64(values[i+1], nil)
606652
case "Total compressions":
607653
info.totalCompressions, outErr = redis.Int64(values[i+1], nil)
608654
}

client_test.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package redis_bloom_go
22

33
import (
4-
"github.com/gomodule/redigo/redis"
5-
"github.com/stretchr/testify/assert"
64
"os"
75
"testing"
86
"time"
7+
8+
"github.com/gomodule/redigo/redis"
9+
"github.com/stretchr/testify/assert"
910
)
1011

1112
func getTestConnectionDetails() (string, string) {
@@ -71,7 +72,7 @@ func TestReserve(t *testing.T) {
7172
"Expansion rate": 2,
7273
"Number of filters": 1,
7374
"Number of items inserted": 0,
74-
"Size": 936,
75+
"Size": 880,
7576
})
7677

7778
err = client.Reserve(key, 0.1, 1000)
@@ -554,16 +555,10 @@ func TestClient_TdReset(t *testing.T) {
554555
ret, err = client.TdReset(key)
555556
assert.Nil(t, err)
556557
assert.Equal(t, "OK", ret)
557-
558-
info, err := client.TdInfo(key)
559-
assert.Nil(t, err)
560-
assert.Equal(t, 0.0, info.UnmergedWeight())
561-
assert.Equal(t, int64(0), info.TotalCompressions())
562-
assert.Equal(t, int64(100), info.Compression())
563-
assert.Equal(t, int64(610), info.Capacity())
564558
}
565559

566560
func TestClient_TdMerge(t *testing.T) {
561+
client.FlushAll()
567562
key1 := "toKey"
568563
key2 := "fromKey"
569564
ret, err := client.TdCreate(key1, 10)
@@ -584,16 +579,17 @@ func TestClient_TdMerge(t *testing.T) {
584579
assert.Equal(t, "OK", ret)
585580

586581
//Merge
587-
ret, err = client.TdMerge(key1, key2)
582+
ret, err = client.TdMerge(key1, 1, key2)
588583
assert.Nil(t, err)
589584
assert.Equal(t, "OK", ret)
590585

586+
// TODO <open question, do we need this>
591587
// we should now have 10 weight on to-histogram
592588
info, err := client.TdInfo(key1)
593589
assert.Nil(t, err)
594-
assert.Equal(t, 10.0, info.UnmergedWeight()+info.MergedWeight())
595-
assert.Equal(t, int64(2), info.UnmergedNodes())
596-
assert.Equal(t, int64(2), info.MergedNodes())
590+
assert.Equal(t, int64(8), info.UnmergedWeight()+info.MergedWeight())
591+
assert.Equal(t, int64(4), info.UnmergedNodes())
592+
assert.Equal(t, int64(4), info.MergedNodes())
597593
}
598594

599595
func TestClient_TdMinMax(t *testing.T) {
@@ -631,11 +627,11 @@ func TestClient_TdQuantile(t *testing.T) {
631627

632628
ans, err := client.TdQuantile(key, 1.0)
633629
assert.Nil(t, err)
634-
assert.Equal(t, 3.0, ans)
630+
assert.Equal(t, 3.0, ans[0])
635631

636632
ans, err = client.TdQuantile(key, 0.0)
637633
assert.Nil(t, err)
638-
assert.Equal(t, 1.0, ans)
634+
assert.Equal(t, 1.0, ans[0])
639635
}
640636

641637
func TestClient_TdCdf(t *testing.T) {
@@ -652,9 +648,9 @@ func TestClient_TdCdf(t *testing.T) {
652648

653649
ans, err := client.TdCdf(key, 10.0)
654650
assert.Nil(t, err)
655-
assert.Equal(t, 1.0, ans)
651+
assert.Equal(t, 1.0, ans[0])
656652

657653
ans, err = client.TdCdf(key, 0.0)
658654
assert.Nil(t, err)
659-
assert.Equal(t, 0.0, ans)
655+
assert.Equal(t, 0.0, ans[0])
660656
}

example_client_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package redis_bloom_go_test
22

33
import (
44
"fmt"
5+
"log"
6+
57
redisbloom "github.com/RedisBloom/redisbloom-go"
68
"github.com/gomodule/redigo/redis"
7-
"log"
89
)
910

1011
// exemplifies the NewClient function
@@ -55,6 +56,7 @@ func ExampleNewClientFromPool() {
5556
func ExampleClient_TdCreate() {
5657
host := "localhost:6379"
5758
var client = redisbloom.NewClient(host, "nohelp", nil)
59+
client.FlushAll()
5860

5961
ret, err := client.TdCreate("key", 100)
6062
if err != nil {
@@ -70,6 +72,7 @@ func ExampleClient_TdCreate() {
7072
func ExampleClient_TdAdd() {
7173
host := "localhost:6379"
7274
var client = redisbloom.NewClient(host, "nohelp", nil)
75+
client.FlushAll()
7376

7477
key := "example"
7578
ret, err := client.TdCreate(key, 100)
@@ -92,6 +95,7 @@ func ExampleClient_TdAdd() {
9295
func ExampleClient_TdMin() {
9396
host := "localhost:6379"
9497
var client = redisbloom.NewClient(host, "nohelp", nil)
98+
client.FlushAll()
9599

96100
key := "example"
97101
_, err := client.TdCreate(key, 10)
@@ -118,6 +122,7 @@ func ExampleClient_TdMin() {
118122
func ExampleClient_TdMax() {
119123
host := "localhost:6379"
120124
var client = redisbloom.NewClient(host, "nohelp", nil)
125+
client.FlushAll()
121126

122127
key := "example"
123128
_, err := client.TdCreate(key, 10)
@@ -144,6 +149,7 @@ func ExampleClient_TdMax() {
144149
func ExampleClient_TdQuantile() {
145150
host := "localhost:6379"
146151
var client = redisbloom.NewClient(host, "nohelp", nil)
152+
client.FlushAll()
147153

148154
key := "example"
149155
_, err := client.TdCreate(key, 10)
@@ -163,13 +169,14 @@ func ExampleClient_TdQuantile() {
163169
}
164170

165171
fmt.Println(ans)
166-
// Output: 5
172+
// Output: [5]
167173
}
168174

169175
// exemplifies the TdCdf function
170176
func ExampleClient_TdCdf() {
171177
host := "localhost:6379"
172178
var client = redisbloom.NewClient(host, "nohelp", nil)
179+
client.FlushAll()
173180

174181
key := "example"
175182
_, err := client.TdCreate(key, 10)
@@ -189,5 +196,5 @@ func ExampleClient_TdCdf() {
189196
}
190197

191198
fmt.Println(cdf)
192-
// Output: 0.1
199+
// Output: [0.2]
193200
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.12
44

55
require (
66
github.com/davecgh/go-spew v1.1.1 // indirect
7-
github.com/gomodule/redigo v1.8.2
7+
github.com/gomodule/redigo v1.8.9
88
github.com/kr/text v0.2.0 // indirect
99
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
1010
github.com/stretchr/testify v1.7.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
66
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
7+
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
8+
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
79
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
810
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
911
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=

0 commit comments

Comments
 (0)