Skip to content

Commit 9489d36

Browse files
initializing thick client
1 parent d6d7a71 commit 9489d36

File tree

576 files changed

+242137
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

576 files changed

+242137
-5
lines changed

examples/example_ping/main.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,15 @@ func main() {
1515

1616
resp := client.Fire(&wire.Command{Cmd: "PING"})
1717
fmt.Println(resp)
18-
}
18+
19+
resp = client.Fire(&wire.Command{Cmd: "SET", Args: []string{"foo", "bar"}})
20+
fmt.Println(resp)
21+
22+
resp = client.Fire(&wire.Command{Cmd: "GET", Args: []string{"foo"}})
23+
fmt.Println(resp)
24+
25+
resp = client.Fire(&wire.Command{Cmd: "GET", Args: []string{"foo1"}})
26+
fmt.Println(resp)
27+
28+
client.Close()
29+
}

go.mod

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,15 @@ go 1.24.0
44

55
require google.golang.org/protobuf v1.36.5
66

7-
require github.com/google/uuid v1.6.0
7+
require (
8+
github.com/dgraph-io/ristretto v0.2.0
9+
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da
10+
github.com/google/uuid v1.6.0
11+
)
12+
13+
require (
14+
github.com/cespare/xxhash/v2 v2.1.1 // indirect
15+
github.com/dustin/go-humanize v1.0.1 // indirect
16+
github.com/pkg/errors v0.9.1 // indirect
17+
golang.org/x/sys v0.11.0 // indirect
18+
)

go.sum

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
1+
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
2+
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
6+
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
7+
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
8+
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
9+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
10+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
111
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
212
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
313
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
414
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
15+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
16+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
17+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
18+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
19+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
20+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
21+
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
22+
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
523
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
624
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
7-
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
8-
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
25+
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
26+
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
27+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
28+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ package dicedb
33
import (
44
"fmt"
55
"net"
6+
"strconv"
67
"strings"
8+
"sync"
79
"time"
810

11+
"github.com/dgraph-io/ristretto"
12+
"github.com/dgryski/go-farm"
913
"github.com/dicedb/dicedb-go/ironhawk"
1014
"github.com/dicedb/dicedb-go/wire"
1115
"github.com/google/uuid"
16+
"google.golang.org/protobuf/types/known/structpb"
1217
)
1318

1419
type Client struct {
@@ -18,6 +23,9 @@ type Client struct {
1823
watchCh chan *wire.Response
1924
host string
2025
port int
26+
lcache *ristretto.Cache
27+
mu sync.RWMutex // Mutex for thread-safe operations
28+
wg sync.WaitGroup
2129
}
2230

2331
type option func(*Client)
@@ -43,7 +51,17 @@ func NewClient(host string, port int, opts ...option) (*Client, error) {
4351
return nil, err
4452
}
4553

46-
client := &Client{conn: conn, host: host, port: port}
54+
// Initialize Ristretto cache
55+
cache, err := ristretto.NewCache(&ristretto.Config{
56+
NumCounters: 1e7, // number of keys to track frequency of (10M)
57+
MaxCost: 1 << 30, // maximum cost of cache (1GB)
58+
BufferItems: 64, // number of keys per Get buffer
59+
})
60+
if err != nil {
61+
return nil, fmt.Errorf("failed to create Ristretto cache: %w", err)
62+
}
63+
64+
client := &Client{conn: conn, host: host, port: port, lcache: cache}
4765
for _, opt := range opts {
4866
opt(client)
4967
}
@@ -59,6 +77,14 @@ func NewClient(host string, port int, opts ...option) (*Client, error) {
5977
return nil, fmt.Errorf("could not complete the handshake: %s", resp.Err)
6078
}
6179

80+
client.wg.Add(1)
81+
go func(client *Client) {
82+
fmt.Println("Listening for messages")
83+
ListenForMessages(client, func(message string) {
84+
})
85+
client.wg.Done()
86+
}(client)
87+
6288
return client, nil
6389
}
6490

@@ -80,6 +106,44 @@ func (c *Client) fire(cmd *wire.Command, co net.Conn) *wire.Response {
80106
}
81107

82108
func (c *Client) Fire(cmd *wire.Command) *wire.Response {
109+
// TODO: handle this better by refining read-only commands
110+
if cmd.Cmd == "GET" && len(cmd.Args) > 0 {
111+
key := cmd.Args[0]
112+
113+
watch_cmd := &wire.Command{
114+
Cmd: "GET.WATCH",
115+
Args: []string{key},
116+
}
117+
118+
fp := Fingerprint(watch_cmd)
119+
120+
// Check if the key is in the cache
121+
if value, found := c.lcache.Get(fp); found {
122+
return &wire.Response{
123+
Value: &wire.Response_VStr{VStr: value.(string)},
124+
}
125+
}
126+
127+
// If not in cache, send the command to the server
128+
resp := c.fire(cmd, c.conn)
129+
130+
// If the response is successful, store it in the cache
131+
// and subscribe to the key to keep the value updated
132+
if resp.Err == "" {
133+
c.lcache.Set(fp, resp.GetVStr(), 1) // Store only the string value
134+
c.lcache.Wait() // Ensure value is stored before proceeding
135+
136+
c.wg.Add(1)
137+
go func() {
138+
Subscribe(c, key)
139+
c.wg.Done()
140+
}()
141+
}
142+
143+
return resp
144+
}
145+
146+
// For non-GET commands, just send to the server
83147
return c.fire(cmd, c.conn)
84148
}
85149

@@ -137,5 +201,51 @@ func (c *Client) watch() {
137201
}
138202

139203
func (c *Client) Close() {
204+
c.wg.Wait()
140205
c.conn.Close()
141206
}
207+
208+
// SubscriptionManager methods
209+
func Subscribe(client *Client, watch_key string) {
210+
resp := client.Fire(&wire.Command{
211+
Cmd: "GET.WATCH",
212+
Args: []string{watch_key},
213+
})
214+
if resp.Err != "" {
215+
fmt.Println("error subscribing:", resp.Err)
216+
}
217+
}
218+
219+
func ListenForMessages(client *Client, onMessage func(message string)) {
220+
ch, err := client.WatchCh()
221+
if err != nil {
222+
panic(err)
223+
}
224+
for resp := range ch {
225+
var fp uint32
226+
// Extract fingerprint from string_value if available
227+
if resp.Attrs != nil {
228+
if fpValue, ok := resp.Attrs.Fields["fingerprint"]; ok {
229+
if fpString, ok := fpValue.Kind.(*structpb.Value_StringValue); ok {
230+
// Convert string fingerprint to uint32
231+
fpUint, err := strconv.ParseUint(fpString.StringValue, 10, 32)
232+
if err == nil {
233+
fp = uint32(fpUint)
234+
}
235+
}
236+
}
237+
}
238+
239+
client.lcache.Set(fp, resp.GetVStr(), 1) // Store only the string value
240+
client.lcache.Wait() // Ensure value is stored before proceeding
241+
onMessage(resp.GetVStr())
242+
}
243+
}
244+
245+
func Fingerprint(c *wire.Command) uint32 {
246+
cmdStr := c.Cmd
247+
if len(c.Args) > 0 {
248+
cmdStr = fmt.Sprintf("%s %s", c.Cmd, strings.Join(c.Args, " "))
249+
}
250+
return farm.Fingerprint32([]byte(cmdStr))
251+
}

vendor/github.com/cespare/xxhash/v2/.travis.yml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/cespare/xxhash/v2/LICENSE.txt

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/cespare/xxhash/v2/README.md

Lines changed: 67 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)