Skip to content

Commit e6c243d

Browse files
committed
refactored into a PacketConn-based server with a custom wire format
1 parent fafb39a commit e6c243d

File tree

9 files changed

+399
-0
lines changed

9 files changed

+399
-0
lines changed

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ require (
1010
)
1111

1212
require (
13+
github.com/billhathaway/consistentHash v0.0.0-20140718022140-addea16d2229 // indirect
1314
github.com/fsnotify/fsnotify v1.6.0 // indirect
1415
github.com/hashicorp/hcl v1.0.0 // indirect
1516
github.com/magiconair/properties v1.8.7 // indirect
1617
github.com/mitchellh/mapstructure v1.5.0 // indirect
1718
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
19+
github.com/spaolacci/murmur3 v1.1.0 // indirect
1820
github.com/spf13/afero v1.9.5 // indirect
1921
github.com/spf13/cast v1.5.1 // indirect
2022
github.com/spf13/jwalterweatherman v1.1.0 // indirect
2123
github.com/spf13/pflag v1.0.5 // indirect
2224
github.com/subosito/gotenv v1.4.2 // indirect
25+
github.com/ugorji/go/codec v1.2.11 // indirect
2326
go.uber.org/atomic v1.11.0 // indirect
2427
go.uber.org/dig v1.17.0 // indirect
2528
go.uber.org/multierr v1.11.0 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
3939
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4040
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
4141
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
42+
github.com/billhathaway/consistentHash v0.0.0-20140718022140-addea16d2229 h1:w1t+UCLwxXgpUcXAlm3IkvWHGJDfhIyNrzJmCUkJq7s=
43+
github.com/billhathaway/consistentHash v0.0.0-20140718022140-addea16d2229/go.mod h1:YTos5xiYv+RiIsYn3pqdwe5OULySucMqiPes1OgC5pM=
4244
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
4345
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
4446
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@@ -147,6 +149,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
147149
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
148150
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
149151
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
152+
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
153+
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
150154
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
151155
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
152156
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
@@ -170,6 +174,8 @@ github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gt
170174
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
171175
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
172176
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
177+
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
178+
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
173179
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
174180
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
175181
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

pkg/hashy/config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package hashy
2+
3+
type Config struct {
4+
Address string
5+
Network string
6+
MaxPacketSize int
7+
MaxConcurrentRequests int
8+
9+
Datacenters map[Datacenter][]string
10+
Vnodes int
11+
}

pkg/hashy/handler.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package hashy
2+
3+
type Handler interface {
4+
ServeHash(ResponseWriter, *Request)
5+
}
6+
7+
type HandlerFunc func(ResponseWriter, *Request)
8+
9+
func (hf HandlerFunc) ServeHash(rw ResponseWriter, r *Request) { hf(rw, r) }
10+
11+
type DefaultHandler struct {
12+
Datacenters map[Datacenter]Hasher
13+
}
14+
15+
func (dh *DefaultHandler) ServeHash(rw ResponseWriter, r *Request) {
16+
for _, name := range r.DeviceNames {
17+
for dc, hasher := range dh.Datacenters {
18+
// TODO: error handling
19+
value, _ := hasher.Get(name.GetHashBytes())
20+
rw.Add(name, dc, value)
21+
}
22+
}
23+
}

pkg/hashy/hasher.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package hashy
2+
3+
import (
4+
"github.com/billhathaway/consistentHash"
5+
"go.uber.org/multierr"
6+
)
7+
8+
type Hasher interface {
9+
Get([]byte) (string, error)
10+
}
11+
12+
func NewHasher(vnodes int, values []string) (Hasher, error) {
13+
ch := consistentHash.New()
14+
if err := ch.SetVnodeCount(vnodes); err != nil {
15+
return nil, err
16+
}
17+
18+
for _, v := range values {
19+
ch.Add(v)
20+
}
21+
22+
return ch, nil
23+
}
24+
25+
func NewDatacenterHashers(cfg Config) (m map[Datacenter]Hasher, err error) {
26+
m = make(map[Datacenter]Hasher, len(cfg.Datacenters))
27+
for dc, values := range cfg.Datacenters {
28+
h, hErr := NewHasher(cfg.Vnodes, values)
29+
err = multierr.Append(err, hErr)
30+
if hErr == nil {
31+
m[dc] = h
32+
}
33+
}
34+
35+
return
36+
}

pkg/hashy/message.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package hashy
2+
3+
import (
4+
"strings"
5+
6+
"github.com/ugorji/go/codec"
7+
)
8+
9+
var msgpackHandle = codec.MsgpackHandle{}
10+
11+
type Datacenter string
12+
13+
type DeviceName string
14+
15+
func (dn DeviceName) GetHashBytes() []byte {
16+
i := strings.IndexRune(string(dn), ':')
17+
if i >= 0 {
18+
return []byte(dn[i+1:])
19+
}
20+
21+
return []byte(dn)
22+
}
23+
24+
type DeviceNames []DeviceName
25+
26+
type DeviceHashes map[DeviceName]map[Datacenter][]string
27+
28+
func (dh *DeviceHashes) Add(name DeviceName, dc Datacenter, value string) {
29+
if *dh == nil {
30+
*dh = DeviceHashes{
31+
name: map[Datacenter][]string{
32+
dc: []string{value},
33+
},
34+
}
35+
36+
return
37+
}
38+
39+
if datacenters, exists := (*dh)[name]; exists {
40+
datacenters[dc] = append(datacenters[dc], value)
41+
} else {
42+
(*dh)[name] = map[Datacenter][]string{
43+
dc: []string{value},
44+
}
45+
}
46+
}
47+
48+
func UnmarshalBytes[T any](b []byte) (t T, err error) {
49+
decoder := codec.NewDecoderBytes(b, &msgpackHandle)
50+
err = decoder.Decode(&t)
51+
return
52+
}
53+
54+
func MarshalBytes[T any](t T) (b []byte, err error) {
55+
encoder := codec.NewEncoderBytes(&b, &msgpackHandle)
56+
err = encoder.Encode(t)
57+
return
58+
}

pkg/hashy/request.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package hashy
2+
3+
import (
4+
"context"
5+
"net"
6+
)
7+
8+
type Request struct {
9+
RemoteAddr net.Addr
10+
DeviceNames DeviceNames
11+
12+
ctx context.Context
13+
}
14+
15+
func (r *Request) Context() context.Context {
16+
if r.ctx == nil {
17+
return context.Background()
18+
}
19+
20+
return r.ctx
21+
}
22+
23+
func NewRequest(names DeviceNames) *Request {
24+
return &Request{
25+
DeviceNames: names,
26+
ctx: context.Background(),
27+
}
28+
}

pkg/hashy/responseWriter.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package hashy
2+
3+
import (
4+
"net"
5+
"sync"
6+
)
7+
8+
type Flusher interface {
9+
Flush() error
10+
}
11+
12+
// Writer defines the behavior of a packet-oriented writer.
13+
type Writer interface {
14+
// WriteTo has the same contract as net.PacketConn.WriteTo.
15+
WriteTo([]byte, net.Addr) (int, error)
16+
}
17+
18+
type syncWriter struct {
19+
lock sync.Mutex
20+
w Writer
21+
}
22+
23+
func (sw *syncWriter) WriteTo(b []byte, a net.Addr) (int, error) {
24+
defer sw.lock.Unlock()
25+
sw.lock.Lock()
26+
return sw.w.WriteTo(b, a)
27+
}
28+
29+
type ResponseWriter interface {
30+
Flusher
31+
Add(DeviceName, Datacenter, string)
32+
}
33+
34+
type responseWriter struct {
35+
remoteAddr net.Addr
36+
writer Writer
37+
hashes DeviceHashes
38+
}
39+
40+
func (rw *responseWriter) Add(name DeviceName, dc Datacenter, value string) {
41+
rw.hashes.Add(name, dc, value)
42+
}
43+
44+
func (rw *responseWriter) Flush() (err error) {
45+
var message []byte
46+
message, err = MarshalBytes(rw.hashes)
47+
if err == nil {
48+
_, err = rw.writer.WriteTo(message, rw.remoteAddr)
49+
}
50+
51+
return
52+
}

0 commit comments

Comments
 (0)