DynamoDB/client/client.go

93 lines
1.8 KiB
Go

package client
import (
"bytes"
"errors"
"io/ioutil"
"log"
"net"
"net/http"
"strings"
"github.com/buraksezer/consistent"
"github.com/cespare/xxhash"
)
type Client struct {
hash *consistent.Consistent
}
type hasher struct{}
func (h hasher) Sum64(data []byte) uint64 {
return xxhash.Sum64(data)
}
type netAddr string
func (na netAddr) String() string {
return string(na)
}
func (na netAddr) Network() string {
return "tcp"
}
func New(addrs ...string) (*Client, error) {
cfg := consistent.Config{
PartitionCount: 71,
ReplicationFactor: 20,
Load: 1.25,
Hasher: hasher{},
}
hash := consistent.New(nil, cfg)
for _, addr := range addrs {
hash.Add(netAddr(addr))
}
for _, addr := range hash.GetMembers() {
conn, err := net.Dial("tcp", strings.TrimPrefix(strings.TrimPrefix(addr.String(), "http://"), "https://"))
if err != nil {
return nil, err
}
conn.Close()
}
return &Client{
hash: hash,
}, nil
}
func (c *Client) Get(key string) ([]byte, error) {
addr := c.hash.LocateKey([]byte(key)).String()
log.Printf("GET %s FROM %s", key, addr)
resp, err := http.Get(addr + "/" + key)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("bad status on get")
}
return ioutil.ReadAll(resp.Body)
}
func (c *Client) Set(key string, value []byte) error {
addr := c.hash.LocateKey([]byte(key)).String()
log.Printf("SET %s FROM %s", key, addr)
r, err := http.NewRequest("PUT", addr+"/"+key, bytes.NewBuffer(value))
if err != nil {
return err
}
resp, err := (&http.Client{}).Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New("bad status on set")
}
return nil
}