Client scatter gathers by rw concern

master
Bel LaPointe 2019-03-15 11:42:31 -06:00
parent 648ce19313
commit 232fa86c61
3 changed files with 103 additions and 8 deletions

View File

@ -5,14 +5,32 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"strconv"
"strings" "strings"
) )
func main() { func main() {
client, err := New(strings.Split(os.Getenv("ADDR"), ",")...) rConcernS, ok := os.LookupEnv("RCONCERN")
if !ok {
rConcernS = "1"
}
wConcernS, ok := os.LookupEnv("WCONCERN")
if !ok {
wConcernS = "1"
}
rConcern, err := strconv.Atoi(rConcernS)
if err != nil { if err != nil {
panic(err) panic(err)
} }
wConcern, err := strconv.Atoi(wConcernS)
if err != nil {
panic(err)
}
client, err := New(rConcern, wConcern, strings.Split(os.Getenv("ADDR"), ",")...)
if err != nil {
panic(err)
}
log.Printf("client: %v", client)
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
for { for {
fmt.Print("> ") fmt.Print("> ")
@ -46,7 +64,7 @@ func get(client *Client, line string) {
words := strings.Split(line, " ") words := strings.Split(line, " ")
key := words[1] key := words[1]
v, err := client.Get(key) v, err := client.Get(key)
log.Printf("set: %v: %s", err, v) log.Printf("get: %v: %s", err, v)
} }
func set(client *Client, line string) { func set(client *Client, line string) {

View File

@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net" "net"
@ -15,6 +16,8 @@ import (
type Client struct { type Client struct {
hash *consistent.Consistent hash *consistent.Consistent
wConcern int
rConcern int
} }
type hasher struct{} type hasher struct{}
@ -33,7 +36,31 @@ func (na netAddr) Network() string {
return "tcp" return "tcp"
} }
func New(addrs ...string) (*Client, error) { type result struct {
b []byte
err error
}
func (r result) String() string {
return fmt.Sprintf("%v : %v", r.err, r.b)
}
func New(rConcern, wConcern int, addrs ...string) (*Client, error) {
if rConcern > len(addrs)-1 {
rConcern = len(addrs) - 1
}
if wConcern > len(addrs)-1 {
wConcern = len(addrs) - 1
}
if rConcern > wConcern {
rConcern = wConcern
}
if rConcern < 1 {
rConcern = 1
}
if wConcern < 1 {
wConcern = 1
}
cfg := consistent.Config{ cfg := consistent.Config{
PartitionCount: 71, PartitionCount: 71,
ReplicationFactor: 20, ReplicationFactor: 20,
@ -53,11 +80,54 @@ func New(addrs ...string) (*Client, error) {
} }
return &Client{ return &Client{
hash: hash, hash: hash,
wConcern: wConcern,
rConcern: rConcern,
}, nil }, nil
} }
func (c *Client) scatterGather(key string, forEach func(addr string, each chan result), try, need int) ([]byte, error) {
final := make(chan result)
each := make(chan result)
members, err := c.hash.GetClosestN([]byte(key), try)
if err != nil {
return nil, err
}
for _, member := range members {
go forEach(member.String(), each)
}
go func() {
out := make(map[string]int)
done := false
for i := 0; i < len(members); i++ {
one := <-each
if done {
continue
}
if _, ok := out[one.String()]; !ok {
out[one.String()] = 0
}
out[one.String()] += 1
if out[one.String()] >= need {
final <- one
done = true
}
}
if !done {
final <- result{err: errors.New("no consensus")}
}
}()
consensus := <-final
return consensus.b, consensus.err
}
func (c *Client) Get(key string) ([]byte, error) { func (c *Client) Get(key string) ([]byte, error) {
addr := c.hash.LocateKey([]byte(key)).String() return c.scatterGather(key, func(addr string, each chan result) {
b, err := c.get(addr, key)
each <- result{b: b, err: err}
}, c.wConcern, c.rConcern)
}
func (c *Client) get(addr, key string) ([]byte, error) {
log.Printf("GET %s FROM %s", key, addr) log.Printf("GET %s FROM %s", key, addr)
resp, err := http.Get(addr + "/" + key) resp, err := http.Get(addr + "/" + key)
if err != nil { if err != nil {
@ -74,7 +144,14 @@ func (c *Client) Get(key string) ([]byte, error) {
} }
func (c *Client) Set(key string, value []byte) error { func (c *Client) Set(key string, value []byte) error {
addr := c.hash.LocateKey([]byte(key)).String() _, err := c.scatterGather(key, func(addr string, each chan result) {
err := c.set(addr, key, value)
each <- result{err: err}
}, c.wConcern, c.rConcern)
return err
}
func (c *Client) set(addr, key string, value []byte) error {
log.Printf("SET %s FROM %s", key, addr) log.Printf("SET %s FROM %s", key, addr)
r, err := http.NewRequest("PUT", addr+"/"+key, bytes.NewBuffer(value)) r, err := http.NewRequest("PUT", addr+"/"+key, bytes.NewBuffer(value))
if err != nil { if err != nil {

View File

@ -23,7 +23,7 @@ func TestAll(t *testing.T) {
go s.Run() go s.Run()
} }
client, err := New(addresses...) client, err := New(1, 1, addresses...)
if err != nil { if err != nil {
t.Fatalf("cannot make client: %v", err) t.Fatalf("cannot make client: %v", err)
} }