Commit f6d22396 authored by Erik Wilson's avatar Erik Wilson

Upgrade go-dqlite to v1.3.0

parent 9764a614
...@@ -64,7 +64,7 @@ require ( ...@@ -64,7 +64,7 @@ require (
github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e // indirect github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e // indirect
github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect
github.com/canonical/go-dqlite v1.2.0 github.com/canonical/go-dqlite v1.3.0
github.com/containerd/cgroups v0.0.0-00010101000000-000000000000 // indirect github.com/containerd/cgroups v0.0.0-00010101000000-000000000000 // indirect
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69 github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
......
...@@ -116,6 +116,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 ...@@ -116,6 +116,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E= github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
github.com/canonical/go-dqlite v1.2.0 h1:TCsNV/mAmPy7PQa4jIQMoiXbCUVqYTSv6OJSpi/qdX8= github.com/canonical/go-dqlite v1.2.0 h1:TCsNV/mAmPy7PQa4jIQMoiXbCUVqYTSv6OJSpi/qdX8=
github.com/canonical/go-dqlite v1.2.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y= github.com/canonical/go-dqlite v1.2.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
github.com/canonical/go-dqlite v1.3.0 h1:c+7eGZfh0K7yCmGrBkNRGZdY8R8+2jSSkz6Zr3YCjJE=
github.com/canonical/go-dqlite v1.3.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
...@@ -713,12 +715,6 @@ github.com/rancher/dynamiclistener v0.2.0 h1:KucYwJXVVGhZ/NndfMCeQoCafT/VN7kvqSG ...@@ -713,12 +715,6 @@ github.com/rancher/dynamiclistener v0.2.0 h1:KucYwJXVVGhZ/NndfMCeQoCafT/VN7kvqSG
github.com/rancher/dynamiclistener v0.2.0/go.mod h1:fs/dxyNcB3YT6W9fVz4bDGfhmSQS17QQup6BIcGF++s= github.com/rancher/dynamiclistener v0.2.0/go.mod h1:fs/dxyNcB3YT6W9fVz4bDGfhmSQS17QQup6BIcGF++s=
github.com/rancher/flannel v0.11.0-k3s.1 h1:mIwnfWDafjzQgFkZeJ1AkFrrAT3EdBaA1giE0eLJKo8= github.com/rancher/flannel v0.11.0-k3s.1 h1:mIwnfWDafjzQgFkZeJ1AkFrrAT3EdBaA1giE0eLJKo8=
github.com/rancher/flannel v0.11.0-k3s.1/go.mod h1:Hn4ZV+eq0LhLZP63xZnxdGwXEoRSxs5sxELxu27M3UA= github.com/rancher/flannel v0.11.0-k3s.1/go.mod h1:Hn4ZV+eq0LhLZP63xZnxdGwXEoRSxs5sxELxu27M3UA=
github.com/rancher/helm-controller v0.3.0 h1:sYRpOiJc4+NmSEkft3lR2pwaEPOrPzZOTo2UjFnVF4I=
github.com/rancher/helm-controller v0.3.0/go.mod h1:194LHuZRrxcD82bG1rJtOWsw98U4JbPhDWqvL7l3PAw=
github.com/rancher/helm-controller v0.4.0 h1:VO7TqMsMWh3TJK7U38LLht1uHiDy0fSYWSywT0bE5lY=
github.com/rancher/helm-controller v0.4.0/go.mod h1:194LHuZRrxcD82bG1rJtOWsw98U4JbPhDWqvL7l3PAw=
github.com/rancher/helm-controller v0.4.1-0.20191219210749-b81cad6ffd60 h1:c4UL7AcwFGoZIit6EoVS9S1ylj4IGhPCtQM23iFXBzA=
github.com/rancher/helm-controller v0.4.1-0.20191219210749-b81cad6ffd60/go.mod h1:194LHuZRrxcD82bG1rJtOWsw98U4JbPhDWqvL7l3PAw=
github.com/rancher/helm-controller v0.4.1-0.20191223195946-ae918063bc54 h1:S/IAloMp6MYYtoHKfJUS5M3Wv0CU+5Ixe1nnSv2pQUU= github.com/rancher/helm-controller v0.4.1-0.20191223195946-ae918063bc54 h1:S/IAloMp6MYYtoHKfJUS5M3Wv0CU+5Ixe1nnSv2pQUU=
github.com/rancher/helm-controller v0.4.1-0.20191223195946-ae918063bc54/go.mod h1:194LHuZRrxcD82bG1rJtOWsw98U4JbPhDWqvL7l3PAw= github.com/rancher/helm-controller v0.4.1-0.20191223195946-ae918063bc54/go.mod h1:194LHuZRrxcD82bG1rJtOWsw98U4JbPhDWqvL7l3PAw=
github.com/rancher/kine v0.3.2 h1:2kP48ojBWVoZ6vlzixc9jc9uKRk7Yn1a7kWoOsJi7Sg= github.com/rancher/kine v0.3.2 h1:2kP48ojBWVoZ6vlzixc9jc9uKRk7Yn1a7kWoOsJi7Sg=
......
;;; Directory Local Variables ;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables") ;;; For more information see (info "(emacs) Directory Variables")
((go-mode ((go-mode
. ((go-test-args . "-tags libsqlite3 -timeout 10s") . ((go-test-args . "-tags libsqlite3 -timeout 60s")
(eval (eval
. (set . (set
(make-local-variable 'flycheck-go-build-tags) (make-local-variable 'flycheck-go-build-tags)
......
dist: xenial dist: bionic
language: go language: go
addons: addons:
......
...@@ -107,7 +107,7 @@ func (c *Client) Cluster(ctx context.Context) ([]NodeInfo, error) { ...@@ -107,7 +107,7 @@ func (c *Client) Cluster(ctx context.Context) ([]NodeInfo, error) {
response := protocol.Message{} response := protocol.Message{}
response.Init(512) response.Init(512)
protocol.EncodeCluster(&request) protocol.EncodeCluster(&request, protocol.ClusterFormatV1)
if err := c.protocol.Call(ctx, &request, &response); err != nil { if err := c.protocol.Call(ctx, &request, &response); err != nil {
return nil, errors.Wrap(err, "failed to send Cluster request") return nil, errors.Wrap(err, "failed to send Cluster request")
...@@ -163,24 +163,86 @@ func (c *Client) Dump(ctx context.Context, dbname string) ([]File, error) { ...@@ -163,24 +163,86 @@ func (c *Client) Dump(ctx context.Context, dbname string) ([]File, error) {
} }
// Add a node to a cluster. // Add a node to a cluster.
//
// The new node will have the role specified in node.Role. Note that if the
// desired role is Voter, the node being added must be online, since it will be
// granted voting rights only once it catches up with the leader's log.
func (c *Client) Add(ctx context.Context, node NodeInfo) error { func (c *Client) Add(ctx context.Context, node NodeInfo) error {
request := protocol.Message{} request := protocol.Message{}
response := protocol.Message{}
request.Init(4096) request.Init(4096)
response.Init(4096)
protocol.EncodeAdd(&request, node.ID, node.Address)
if err := c.protocol.Call(ctx, &request, &response); err != nil {
return err
}
if err := protocol.DecodeEmpty(&response); err != nil {
return err
}
// If the desired role is spare, there's nothing to do, since all newly
// added nodes have the spare role.
if node.Role == Spare {
return nil
}
return c.Assign(ctx, node.ID, node.Role)
}
// Assign a role to a node.
//
// Possible roles are:
//
// - Voter: the node will replicate data and participate in quorum.
// - StandBy: the node will replicate data but won't participate in quorum.
// - Spare: the node won't replicate data and won't participate in quorum.
//
// If the target node does not exist or has already the desired role, an error
// is returned.
func (c *Client) Assign(ctx context.Context, id uint64, role NodeRole) error {
request := protocol.Message{}
response := protocol.Message{} response := protocol.Message{}
request.Init(4096)
response.Init(4096) response.Init(4096)
protocol.EncodeJoin(&request, node.ID, node.Address) protocol.EncodeAssign(&request, id, uint64(role))
if err := c.protocol.Call(ctx, &request, &response); err != nil { if err := c.protocol.Call(ctx, &request, &response); err != nil {
return err return err
} }
protocol.EncodePromote(&request, node.ID) if err := protocol.DecodeEmpty(&response); err != nil {
return err
}
return nil
}
// Transfer leadership from the current leader to another node.
//
// This must be invoked one client connected to the current leader.
func (c *Client) Transfer(ctx context.Context, id uint64) error {
request := protocol.Message{}
response := protocol.Message{}
request.Init(4096)
response.Init(4096)
protocol.EncodeTransfer(&request, id)
if err := c.protocol.Call(ctx, &request, &response); err != nil { if err := c.protocol.Call(ctx, &request, &response); err != nil {
return err return err
} }
if err := protocol.DecodeEmpty(&response); err != nil {
return err
}
return nil return nil
} }
......
package client
import (
"github.com/canonical/go-dqlite/internal/protocol"
)
// Node roles
const (
Voter = protocol.Voter
StandBy = protocol.StandBy
Spare = protocol.Spare
)
...@@ -15,6 +15,9 @@ import ( ...@@ -15,6 +15,9 @@ import (
// dqlite nodes that it can dial in order to find a leader dqlite node to use. // dqlite nodes that it can dial in order to find a leader dqlite node to use.
type NodeStore = protocol.NodeStore type NodeStore = protocol.NodeStore
// NodeRole identifies the role of a node.
type NodeRole = protocol.NodeRole
// NodeInfo holds information about a single server. // NodeInfo holds information about a single server.
type NodeInfo = protocol.NodeInfo type NodeInfo = protocol.NodeInfo
...@@ -30,6 +33,7 @@ type DatabaseNodeStore struct { ...@@ -30,6 +33,7 @@ type DatabaseNodeStore struct {
schema string // Name of the schema holding the servers table. schema string // Name of the schema holding the servers table.
table string // Name of the servers table. table string // Name of the servers table.
column string // Column name in the servers table holding the server address. column string // Column name in the servers table holding the server address.
where string // Optional WHERE filter
} }
// DefaultNodeStore creates a new NodeStore using the given filename to // DefaultNodeStore creates a new NodeStore using the given filename to
...@@ -59,13 +63,35 @@ func DefaultNodeStore(filename string) (*DatabaseNodeStore, error) { ...@@ -59,13 +63,35 @@ func DefaultNodeStore(filename string) (*DatabaseNodeStore, error) {
return store, nil return store, nil
} }
// Option that can be used to tweak node store parameters.
type NodeStoreOption func(*nodeStoreOptions)
type nodeStoreOptions struct {
Where string
}
// WithNodeStoreWhereClause configures the node store to append the given
// hard-coded where clause to the SELECT query used to fetch nodes. Only the
// clause itself must be given, without the "WHERE" prefix.
func WithNodeStoreWhereClause(where string) NodeStoreOption {
return func(options *nodeStoreOptions) {
options.Where = where
}
}
// NewNodeStore creates a new NodeStore. // NewNodeStore creates a new NodeStore.
func NewNodeStore(db *sql.DB, schema, table, column string) *DatabaseNodeStore { func NewNodeStore(db *sql.DB, schema, table, column string, options ...NodeStoreOption) *DatabaseNodeStore {
o := &nodeStoreOptions{}
for _, option := range options {
option(o)
}
return &DatabaseNodeStore{ return &DatabaseNodeStore{
db: db, db: db,
schema: schema, schema: schema,
table: table, table: table,
column: column, column: column,
where: o.Where,
} }
} }
...@@ -78,6 +104,9 @@ func (d *DatabaseNodeStore) Get(ctx context.Context) ([]NodeInfo, error) { ...@@ -78,6 +104,9 @@ func (d *DatabaseNodeStore) Get(ctx context.Context) ([]NodeInfo, error) {
defer tx.Rollback() defer tx.Rollback()
query := fmt.Sprintf("SELECT %s FROM %s.%s", d.column, d.schema, d.table) query := fmt.Sprintf("SELECT %s FROM %s.%s", d.column, d.schema, d.table)
if d.where != "" {
query += " WHERE " + d.where
}
rows, err := tx.QueryContext(ctx, query) rows, err := tx.QueryContext(ctx, query)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to query servers table") return nil, errors.Wrap(err, "failed to query servers table")
......
...@@ -18,8 +18,10 @@ import ( ...@@ -18,8 +18,10 @@ import (
"context" "context"
"database/sql/driver" "database/sql/driver"
"io" "io"
"log"
"net" "net"
"reflect" "reflect"
"syscall"
"time" "time"
"github.com/Rican7/retry/backoff" "github.com/Rican7/retry/backoff"
...@@ -624,8 +626,14 @@ func (r *Rows) ColumnTypeDatabaseTypeName(i int) string { ...@@ -624,8 +626,14 @@ func (r *Rows) ColumnTypeDatabaseTypeName(i int) string {
if r.types == nil { if r.types == nil {
var err error var err error
r.types, err = r.rows.ColumnTypes() r.types, err = r.rows.ColumnTypes()
if err != nil { // an error might not matter if we get our types
panic(err) if err != nil && i >= len(r.types) {
// a panic here doesn't really help,
// as an empty column type is not the end of the world
// but we should still inform the user of the failure
const msg = "row (%p) error returning column #%d type: %v\n"
log.Printf(msg, r, i, err)
return ""
} }
} }
return r.types[i] return r.types[i]
...@@ -645,6 +653,8 @@ func valuesToNamedValues(args []driver.Value) []driver.NamedValue { ...@@ -645,6 +653,8 @@ func valuesToNamedValues(args []driver.Value) []driver.NamedValue {
func driverError(err error) error { func driverError(err error) error {
switch err := errors.Cause(err).(type) { switch err := errors.Cause(err).(type) {
case syscall.Errno:
return driver.ErrBadConn
case *net.OpError: case *net.OpError:
return driver.ErrBadConn return driver.ErrBadConn
case protocol.ErrRequest: case protocol.ErrRequest:
......
...@@ -50,7 +50,7 @@ static dqlite_node_info *makeInfos(int n) { ...@@ -50,7 +50,7 @@ static dqlite_node_info *makeInfos(int n) {
return calloc(n, sizeof(dqlite_node_info)); return calloc(n, sizeof(dqlite_node_info));
} }
static void setInfo(dqlite_node_info *infos, unsigned i, unsigned id, const char *address) { static void setInfo(dqlite_node_info *infos, unsigned i, dqlite_node_id id, const char *address) {
dqlite_node_info *info = &infos[i]; dqlite_node_info *info = &infos[i];
info->id = id; info->id = id;
info->address = address; info->address = address;
...@@ -105,7 +105,7 @@ func ConfigMultiThread() error { ...@@ -105,7 +105,7 @@ func ConfigMultiThread() error {
// NewNode creates a new Node instance. // NewNode creates a new Node instance.
func NewNode(id uint64, address string, dir string) (*Node, error) { func NewNode(id uint64, address string, dir string) (*Node, error) {
var server *C.dqlite_node var server *C.dqlite_node
cid := C.unsigned(id) cid := C.dqlite_node_id(id)
caddress := C.CString(address) caddress := C.CString(address)
defer C.free(unsafe.Pointer(caddress)) defer C.free(unsafe.Pointer(caddress))
...@@ -186,7 +186,7 @@ func (s *Node) Recover(cluster []protocol.NodeInfo) error { ...@@ -186,7 +186,7 @@ func (s *Node) Recover(cluster []protocol.NodeInfo) error {
infos := C.makeInfos(n) infos := C.makeInfos(n)
defer C.free(unsafe.Pointer(infos)) defer C.free(unsafe.Pointer(infos))
for i, info := range cluster { for i, info := range cluster {
cid := C.unsigned(info.ID) cid := C.dqlite_node_id(info.ID)
caddress := C.CString(info.Address) caddress := C.CString(info.Address)
defer C.free(unsafe.Pointer(caddress)) defer C.free(unsafe.Pointer(caddress))
C.setInfo(infos, C.unsigned(i), cid, caddress) C.setInfo(infos, C.unsigned(i), cid, caddress)
......
...@@ -6,6 +6,19 @@ const VersionOne = uint64(1) ...@@ -6,6 +6,19 @@ const VersionOne = uint64(1)
// VersionLegacy is the pre 1.0 dqlite server protocol version. // VersionLegacy is the pre 1.0 dqlite server protocol version.
const VersionLegacy = uint64(0x86104dd760433fe5) const VersionLegacy = uint64(0x86104dd760433fe5)
// Cluster response formats
const (
ClusterFormatV0 = 0
ClusterFormatV1 = 1
)
// Node roles
const (
Voter = NodeRole(0)
StandBy = NodeRole(1)
Spare = NodeRole(2)
)
// SQLite datatype codes // SQLite datatype codes
const ( const (
Integer = 1 Integer = 1
...@@ -35,11 +48,12 @@ const ( ...@@ -35,11 +48,12 @@ const (
RequestExecSQL = 8 RequestExecSQL = 8
RequestQuerySQL = 9 RequestQuerySQL = 9
RequestInterrupt = 10 RequestInterrupt = 10
RequestJoin = 12 RequestAdd = 12
RequestPromote = 13 RequestAssign = 13
RequestRemove = 14 RequestRemove = 14
RequestDump = 15 RequestDump = 15
RequestCluster = 16 RequestCluster = 16
RequestTransfer = 17
) )
// Response types. // Response types.
......
...@@ -425,6 +425,7 @@ func (m *Message) getNodes() Nodes { ...@@ -425,6 +425,7 @@ func (m *Message) getNodes() Nodes {
for i := 0; i < int(n); i++ { for i := 0; i < int(n); i++ {
servers[i].ID = m.getUint64() servers[i].ID = m.getUint64()
servers[i].Address = m.getString() servers[i].Address = m.getString()
servers[i].Role = NodeRole(m.getUint64())
} }
return servers return servers
...@@ -501,16 +502,25 @@ type Result struct { ...@@ -501,16 +502,25 @@ type Result struct {
type Rows struct { type Rows struct {
Columns []string Columns []string
message *Message message *Message
types []uint8
} }
// columnTypes returns the row's column types // columnTypes returns the row's column types
// if save is true, it will restore the buffer offset // if save is true, it will restore the buffer offset
func (r *Rows) columnTypes(save bool) ([]uint8, error) { func (r *Rows) columnTypes(save bool) ([]uint8, error) {
types := make([]uint8, len(r.Columns)) // use cached values if possible if not advancing the buffer offset
if save && r.types != nil {
return r.types, nil
}
// column types should never change between rows
// use cached copy to allow getting types when no more rows
if r.types == nil {
r.types = make([]uint8, len(r.Columns))
}
// Each column needs a 4 byte slot to store the column type. The row // Each column needs a 4 byte slot to store the column type. The row
// header must be padded to reach word boundary. // header must be padded to reach word boundary.
headerBits := len(types) * 4 headerBits := len(r.types) * 4
padBits := 0 padBits := 0
if trailingBits := (headerBits % messageWordBits); trailingBits != 0 { if trailingBits := (headerBits % messageWordBits); trailingBits != 0 {
padBits = (messageWordBits - trailingBits) padBits = (messageWordBits - trailingBits)
...@@ -523,34 +533,40 @@ func (r *Rows) columnTypes(save bool) ([]uint8, error) { ...@@ -523,34 +533,40 @@ func (r *Rows) columnTypes(save bool) ([]uint8, error) {
if slot == 0xee { if slot == 0xee {
// More rows are available. // More rows are available.
return nil, ErrRowsPart if save {
r.message.bufferForGet().Advance(-(i + 1))
}
return r.types, ErrRowsPart
} }
if slot == 0xff { if slot == 0xff {
// Rows EOF marker // Rows EOF marker
return nil, io.EOF if save {
r.message.bufferForGet().Advance(-(i + 1))
}
return r.types, io.EOF
} }
index := i * 2 index := i * 2
if index >= len(types) { if index >= len(r.types) {
continue // This is padding. continue // This is padding.
} }
types[index] = slot & 0x0f r.types[index] = slot & 0x0f
index++ index++
if index >= len(types) { if index >= len(r.types) {
continue // This is padding byte. continue // This is padding byte.
} }
types[index] = slot >> 4 r.types[index] = slot >> 4
} }
if save { if save {
r.message.bufferForGet().Advance(-headerSize) r.message.bufferForGet().Advance(-headerSize)
} }
return types, nil return r.types, nil
} }
// Next returns the next row in the result set. // Next returns the next row in the result set.
...@@ -675,10 +691,7 @@ var iso8601Formats = []string{ ...@@ -675,10 +691,7 @@ var iso8601Formats = []string{
// ColumnTypes returns the column types for the the result set. // ColumnTypes returns the column types for the the result set.
func (r *Rows) ColumnTypes() ([]string, error) { func (r *Rows) ColumnTypes() ([]string, error) {
types, err := r.columnTypes(true) types, err := r.columnTypes(true)
if err != nil { kinds := make([]string, len(types))
return nil, err
}
kinds := make([]string, len(r.Columns))
for i, t := range types { for i, t := range types {
switch t { switch t {
...@@ -703,5 +716,5 @@ func (r *Rows) ColumnTypes() ([]string, error) { ...@@ -703,5 +716,5 @@ func (r *Rows) ColumnTypes() ([]string, error) {
} }
} }
return kinds, nil return kinds, err
} }
...@@ -94,19 +94,20 @@ func EncodeInterrupt(request *Message, db uint64) { ...@@ -94,19 +94,20 @@ func EncodeInterrupt(request *Message, db uint64) {
request.putHeader(RequestInterrupt) request.putHeader(RequestInterrupt)
} }
// EncodeJoin encodes a Join request. // EncodeAdd encodes a Add request.
func EncodeJoin(request *Message, id uint64, address string) { func EncodeAdd(request *Message, id uint64, address string) {
request.putUint64(id) request.putUint64(id)
request.putString(address) request.putString(address)
request.putHeader(RequestJoin) request.putHeader(RequestAdd)
} }
// EncodePromote encodes a Promote request. // EncodeAssign encodes a Assign request.
func EncodePromote(request *Message, id uint64) { func EncodeAssign(request *Message, id uint64, role uint64) {
request.putUint64(id) request.putUint64(id)
request.putUint64(role)
request.putHeader(RequestPromote) request.putHeader(RequestAssign)
} }
// EncodeRemove encodes a Remove request. // EncodeRemove encodes a Remove request.
...@@ -124,8 +125,15 @@ func EncodeDump(request *Message, name string) { ...@@ -124,8 +125,15 @@ func EncodeDump(request *Message, name string) {
} }
// EncodeCluster encodes a Cluster request. // EncodeCluster encodes a Cluster request.
func EncodeCluster(request *Message) { func EncodeCluster(request *Message, format uint64) {
request.putUint64(0) request.putUint64(format)
request.putHeader(RequestCluster) request.putHeader(RequestCluster)
} }
// EncodeTransfer encodes a Transfer request.
func EncodeTransfer(request *Message, id uint64) {
request.putUint64(id)
request.putHeader(RequestTransfer)
}
...@@ -6,6 +6,7 @@ package protocol ...@@ -6,6 +6,7 @@ package protocol
import "fmt" import "fmt"
// DecodeFailure decodes a Failure response. // DecodeFailure decodes a Failure response.
func DecodeFailure(response *Message) (code uint64, message string, err error) { func DecodeFailure(response *Message) (code uint64, message string, err error) {
mtype, _ := response.getHeader() mtype, _ := response.getHeader()
...@@ -14,13 +15,13 @@ func DecodeFailure(response *Message) (code uint64, message string, err error) { ...@@ -14,13 +15,13 @@ func DecodeFailure(response *Message) (code uint64, message string, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseFailure { if mtype != ResponseFailure {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
code = response.getUint64() code = response.getUint64()
...@@ -37,13 +38,13 @@ func DecodeWelcome(response *Message) (heartbeatTimeout uint64, err error) { ...@@ -37,13 +38,13 @@ func DecodeWelcome(response *Message) (heartbeatTimeout uint64, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseWelcome { if mtype != ResponseWelcome {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
heartbeatTimeout = response.getUint64() heartbeatTimeout = response.getUint64()
...@@ -59,13 +60,13 @@ func DecodeNodeLegacy(response *Message) (address string, err error) { ...@@ -59,13 +60,13 @@ func DecodeNodeLegacy(response *Message) (address string, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseNodeLegacy { if mtype != ResponseNodeLegacy {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
address = response.getString() address = response.getString()
...@@ -81,13 +82,13 @@ func DecodeNode(response *Message) (id uint64, address string, err error) { ...@@ -81,13 +82,13 @@ func DecodeNode(response *Message) (id uint64, address string, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseNode { if mtype != ResponseNode {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
id = response.getUint64() id = response.getUint64()
...@@ -104,13 +105,13 @@ func DecodeNodes(response *Message) (servers Nodes, err error) { ...@@ -104,13 +105,13 @@ func DecodeNodes(response *Message) (servers Nodes, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseNodes { if mtype != ResponseNodes {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
servers = response.getNodes() servers = response.getNodes()
...@@ -126,13 +127,13 @@ func DecodeDb(response *Message) (id uint32, err error) { ...@@ -126,13 +127,13 @@ func DecodeDb(response *Message) (id uint32, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseDb { if mtype != ResponseDb {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
id = response.getUint32() id = response.getUint32()
...@@ -149,13 +150,13 @@ func DecodeStmt(response *Message) (db uint32, id uint32, params uint64, err err ...@@ -149,13 +150,13 @@ func DecodeStmt(response *Message) (db uint32, id uint32, params uint64, err err
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseStmt { if mtype != ResponseStmt {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
db = response.getUint32() db = response.getUint32()
...@@ -173,13 +174,13 @@ func DecodeEmpty(response *Message) (err error) { ...@@ -173,13 +174,13 @@ func DecodeEmpty(response *Message) (err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseEmpty { if mtype != ResponseEmpty {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
response.getUint64() response.getUint64()
...@@ -195,13 +196,13 @@ func DecodeResult(response *Message) (result Result, err error) { ...@@ -195,13 +196,13 @@ func DecodeResult(response *Message) (result Result, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseResult { if mtype != ResponseResult {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
result = response.getResult() result = response.getResult()
...@@ -217,13 +218,13 @@ func DecodeRows(response *Message) (rows Rows, err error) { ...@@ -217,13 +218,13 @@ func DecodeRows(response *Message) (rows Rows, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseRows { if mtype != ResponseRows {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
rows = response.getRows() rows = response.getRows()
...@@ -239,13 +240,13 @@ func DecodeFiles(response *Message) (files Files, err error) { ...@@ -239,13 +240,13 @@ func DecodeFiles(response *Message) (files Files, err error) {
e := ErrRequest{} e := ErrRequest{}
e.Code = response.getUint64() e.Code = response.getUint64()
e.Description = response.getString() e.Description = response.getString()
err = e err = e
return return
} }
if mtype != ResponseFiles { if mtype != ResponseFiles {
err = fmt.Errorf("unexpected response type %d", mtype) err = fmt.Errorf("unexpected response type %d", mtype)
return return
} }
files = response.getFiles() files = response.getFiles()
......
...@@ -12,12 +12,13 @@ package protocol ...@@ -12,12 +12,13 @@ package protocol
//go:generate ./schema.sh --request Finalize db:uint32 stmt:uint32 //go:generate ./schema.sh --request Finalize db:uint32 stmt:uint32
//go:generate ./schema.sh --request ExecSQL db:uint64 sql:string values:NamedValues //go:generate ./schema.sh --request ExecSQL db:uint64 sql:string values:NamedValues
//go:generate ./schema.sh --request QuerySQL db:uint64 sql:string values:NamedValues //go:generate ./schema.sh --request QuerySQL db:uint64 sql:string values:NamedValues
//go:generate ./schema.sh --request Interrupt db:uint64 //go:generate ./schema.sh --request Interrupt db:uint64
//go:generate ./schema.sh --request Join id:uint64 address:string //go:generate ./schema.sh --request Add id:uint64 address:string
//go:generate ./schema.sh --request Promote id:uint64 //go:generate ./schema.sh --request Assign id:uint64 role:uint64
//go:generate ./schema.sh --request Remove id:uint64 //go:generate ./schema.sh --request Remove id:uint64
//go:generate ./schema.sh --request Dump name:string //go:generate ./schema.sh --request Dump name:string
//go:generate ./schema.sh --request Cluster unused:uint64 //go:generate ./schema.sh --request Cluster format:uint64
//go:generate ./schema.sh --request Transfer id:uint64
//go:generate ./schema.sh --response init //go:generate ./schema.sh --response init
//go:generate ./schema.sh --response Failure code:uint64 message:string //go:generate ./schema.sh --response Failure code:uint64 message:string
......
...@@ -4,10 +4,28 @@ import ( ...@@ -4,10 +4,28 @@ import (
"context" "context"
) )
// NodeRole identifies the role of a node.
type NodeRole int
// String implements the Stringer interface.
func (r NodeRole) String() string {
switch r {
case Voter:
return "voter"
case StandBy:
return "stand-by"
case Spare:
return "spare"
default:
return "unknown role"
}
}
// NodeInfo holds information about a single server. // NodeInfo holds information about a single server.
type NodeInfo struct { type NodeInfo struct {
ID uint64 ID uint64
Address string Address string
Role NodeRole
} }
// NodeStore is used by a dqlite client to get an initial list of candidate // NodeStore is used by a dqlite client to get an initial list of candidate
......
...@@ -131,7 +131,7 @@ github.com/blang/semver ...@@ -131,7 +131,7 @@ github.com/blang/semver
github.com/bronze1man/goStrongswanVici github.com/bronze1man/goStrongswanVici
# github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 # github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23
github.com/buger/jsonparser github.com/buger/jsonparser
# github.com/canonical/go-dqlite v1.2.0 # github.com/canonical/go-dqlite v1.3.0
github.com/canonical/go-dqlite github.com/canonical/go-dqlite
github.com/canonical/go-dqlite/client github.com/canonical/go-dqlite/client
github.com/canonical/go-dqlite/driver github.com/canonical/go-dqlite/driver
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment