Skip to content

Commit

Permalink
Initial commit of LogService and HAKeeper (#2970)
Browse files Browse the repository at this point in the history
* logservice: initial commit

* logservice: added LogShardManager

* logservice: renamed service.go -> shards.go

* logservice: added hakeeper and tests

* logservice: some cleanups

* logservice: added stateMachine.LeaseHistory

* logservice: added the ability to filter internal entries

* logservice: fixed various issues in LogStore

* logservice: updated store APIs to expose LogRecord and LSN

* logservice: pass meta info to NodeHost

* logservice: allow NodeHostID to be specified

* logservice: added the ability to query serviceAddress

* logservice: added the ability to query shard info

* logservice: minor update

* logservice: truncate log asynchronously in a worker goroutine

* logservice: fixed copyright notice

* logservice: added heartbeat message ready to be sent to HAKeeper

* logservice: use gogofaster to generate .pb.go

* logservice: minor update to reflect dragonboat v4 changes

* logservice: added pb/rpc

* logservice: added server side rpc support

* logservice: bumped cube version to avoid pebble version conflicts

* logservice: added error handling

* logservice: more error handling

* logservice: added some tests

* logservice: minor fixess for errors.go

* logservice: added DN heartbeat messages

* logservice: minor cleanups

* logservice: minor fix for error handling

* logservice: moved hakeeper code to its own directory

* logservice: made logservice rsm a IStateMachine

* hakeeper: use Lookup() for querying shard ID

* logservice: move pb file to matrixone/proto, some other cleanups

* hakeeper: added hakeeper/state.go

* logservice: added methods for updating hakeeper

* hakeeper: minor fix, added some tests

* logservice: added ticker for hakeeper replica

* logservice: minor fix for service.go

* logservice: fixed some issues in transport.go

* logservice: added client.go

* logservice: minor refactoring

* logservice: added client_test.go, fixed a few bugs

* logservice: readonly clients now reject write requests

* logservice: refactored query shard info API

* logservice: added tests for Servide.GetShardInfo

* logservice: fixed a few static-check reported issues
  • Loading branch information
lni authored Jun 14, 2022
1 parent 9ad4418 commit fa18348
Show file tree
Hide file tree
Showing 27 changed files with 7,609 additions and 28 deletions.
35 changes: 25 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ require (
github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a
github.com/bxcodec/faker/v3 v3.8.0
github.com/cespare/xxhash/v2 v2.1.2
github.com/cockroachdb/pebble v0.0.0-20210526183633-dd2a545f5d75
github.com/cockroachdb/pebble v0.0.0-20220407171941-2120d145e292
github.com/fagongzi/goetty v1.13.0
github.com/fagongzi/util v0.0.0-20210923134909-bccc37b5040d
github.com/go-sql-driver/mysql v1.6.0
github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.6.0
github.com/google/btree v1.0.1
github.com/google/gofuzz v1.2.0
github.com/matrixorigin/matrixcube v0.3.1-0.20220511071845-cfc4bac02bb4
github.com/lni/dragonboat/v4 v4.0.0-20220604123758-e40bf3f57b59
github.com/lni/goutils v1.3.1-0.20220604063047-388d67b4dbc4
github.com/matrixorigin/matrixcube v0.3.1-0.20220606032431-c944d801f1e5
github.com/matrixorigin/simdcsv v0.0.0-20210926114300-591bf748a770
github.com/minio/minio-go/v7 v7.0.27
github.com/panjf2000/ants/v2 v2.4.6
Expand All @@ -39,6 +41,8 @@ require (
)

require (
github.com/VictoriaMetrics/metrics v1.18.1 // indirect
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.12 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.6 // indirect
Expand All @@ -47,15 +51,27 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.6 // indirect
github.com/getsentry/sentry-go v0.12.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
github.com/hashicorp/go-msgpack v0.5.3 // indirect
github.com/hashicorp/go-multierror v1.0.0 // indirect
github.com/hashicorp/go-sockaddr v1.0.0 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/hashicorp/memberlist v0.3.1 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/lni/goutils v1.3.0 // indirect
github.com/miekg/dns v1.1.26 // indirect
github.com/minio/md5-simd v1.1.0 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/rs/xid v1.2.1 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect
github.com/valyala/fastrand v1.1.0 // indirect
github.com/valyala/histogram v1.2.0 // indirect
gopkg.in/ini.v1 v1.57.0 // indirect
)

Expand All @@ -64,10 +80,9 @@ require (
github.com/DataDog/zstd v1.5.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/cockroachdb/errors v1.8.2 // indirect
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f // indirect
github.com/cockroachdb/redact v1.0.8 // indirect
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
github.com/cockroachdb/errors v1.9.0
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
github.com/cockroachdb/redact v1.1.3 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -86,12 +101,12 @@ require (
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/juju/ratelimit v1.0.1 // indirect
github.com/juju/ratelimit v1.0.2-0.20191002062651-f60b32039441 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/klauspost/cpuid/v2 v2.0.3 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lni/vfs v0.2.1-0.20210810090357-27c7525cf64f // indirect
github.com/lni/vfs v0.2.1-0.20220408085249-8be85be1c3c1
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
113 changes: 95 additions & 18 deletions go.sum

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions pkg/common/moerr/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ const (
// Group 2: numeric
DIVIVISION_BY_ZERO = 2000 + iota
OUT_OF_RANGE

// Group 3: invalid input
BAD_CONFIGURATION = 3000 + iota
INVALID_INPUT

// Group 4: unexpected state
INVALID_STATE = 4000 + iota
)

type Error struct {
Expand Down
243 changes: 243 additions & 0 deletions pkg/hakeeper/rsm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// Copyright 2021 - 2022 Matrix Origin
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hakeeper

import (
"encoding/binary"
"encoding/gob"
"io"
"time"

"github.com/lni/dragonboat/v4/logger"
sm "github.com/lni/dragonboat/v4/statemachine"

"github.com/matrixorigin/matrixone/pkg/common/moerr"
"github.com/matrixorigin/matrixone/pkg/pb/logservice"
)

var (
plog = logger.GetLogger("hakeeper")
)

var (
binaryEnc = binary.BigEndian
)

const (
// TickDuration defines the frequency of ticks.
TickDuration = time.Second
// DefaultHAKeeperShardID is the shard ID assigned to the special HAKeeper
// shard.
DefaultHAKeeperShardID uint64 = 0

headerSize = 2
)

const (
createLogShardTag uint16 = iota + 0xAE01
tickTag
dnHeartbeatTag
logHeartbeatTag
)

type logShardIDQuery struct {
name string
}

type logShardIDQueryResult struct {
id uint64
found bool
}

type stateMachine struct {
replicaID uint64

Tick uint64
NextID uint64

LogShards map[string]uint64
DNState DNState
LogState LogState
}

func parseCmdTag(cmd []byte) uint16 {
return binaryEnc.Uint16(cmd)
}

func getCreateLogShardCmd(name string) []byte {
return getLogShardCmd(name, createLogShardTag)
}

func getLogShardCmd(name string, tag uint16) []byte {
cmd := make([]byte, headerSize+len(name))
binaryEnc.PutUint16(cmd, tag)
copy(cmd[headerSize:], []byte(name))
return cmd
}

func isCreateLogShardCmd(cmd []byte) (string, bool) {
return isLogShardCmd(cmd, createLogShardTag)
}

func isDNHeartbeatCmd(cmd []byte) bool {
return isHeartbeatCmd(cmd, dnHeartbeatTag)
}

func isLogHeartbeatCmd(cmd []byte) bool {
return isHeartbeatCmd(cmd, logHeartbeatTag)
}

func isHeartbeatCmd(cmd []byte, tag uint16) bool {
if len(cmd) <= headerSize {
return false
}
return parseCmdTag(cmd) == tag
}

func parseHeartbeatCmd(cmd []byte) []byte {
return cmd[headerSize:]
}

func isLogShardCmd(cmd []byte, tag uint16) (string, bool) {
if len(cmd) <= headerSize {
return "", false
}
if parseCmdTag(cmd) == tag {
return string(cmd[headerSize:]), true
}
return "", false
}

func isTickCmd(cmd []byte) bool {
return len(cmd) == headerSize && binaryEnc.Uint16(cmd) == tickTag
}

func GetTickCmd() []byte {
cmd := make([]byte, headerSize)
binaryEnc.PutUint16(cmd, tickTag)
return cmd
}

func GetLogStoreHeartbeatCmd(data []byte) []byte {
return getHeartbeatCmd(data, logHeartbeatTag)
}

func GetDNStoreHeartbeatCmd(data []byte) []byte {
return getHeartbeatCmd(data, dnHeartbeatTag)
}

func getHeartbeatCmd(data []byte, tag uint16) []byte {
cmd := make([]byte, headerSize+len(data))
binaryEnc.PutUint16(cmd, tag)
copy(cmd[headerSize:], data)
return cmd
}

func NewStateMachine(shardID uint64, replicaID uint64) sm.IStateMachine {
if shardID != DefaultHAKeeperShardID {
panic(moerr.NewError(moerr.INVALID_INPUT, "invalid HAKeeper shard ID"))
}
return &stateMachine{
replicaID: replicaID,
LogShards: make(map[string]uint64),
DNState: NewDNState(),
LogState: NewLogState(),
}
}

func (s *stateMachine) Close() error {
return nil
}

func (s *stateMachine) assignID() uint64 {
s.NextID++
return s.NextID
}

func (s *stateMachine) handleCreateLogShardCmd(cmd []byte) (sm.Result, error) {
name, ok := isCreateLogShardCmd(cmd)
if !ok {
panic(moerr.NewError(moerr.INVALID_INPUT, "not create log shard cmd"))
}
if shardID, ok := s.LogShards[name]; ok {
data := make([]byte, 8)
binaryEnc.PutUint64(data, shardID)
return sm.Result{Value: 0, Data: data}, nil
}
s.LogShards[name] = s.assignID()
return sm.Result{Value: s.NextID}, nil
}

func (s *stateMachine) handleDNHeartbeat(cmd []byte) (sm.Result, error) {
data := parseHeartbeatCmd(cmd)
var hb logservice.DNStoreHeartbeat
if err := hb.Unmarshal(data); err != nil {
panic(err)
}
s.DNState.Update(hb, s.Tick)
return sm.Result{}, nil
}

func (s *stateMachine) handleLogHeartbeat(cmd []byte) (sm.Result, error) {
data := parseHeartbeatCmd(cmd)
var hb logservice.LogStoreHeartbeat
if err := hb.Unmarshal(data); err != nil {
panic(err)
}
s.LogState.Update(hb, s.Tick)
return sm.Result{}, nil
}

func (s *stateMachine) handleTick(cmd []byte) (sm.Result, error) {
s.Tick++
return sm.Result{}, nil
}

func (s *stateMachine) Update(e sm.Entry) (sm.Result, error) {
cmd := e.Cmd
if _, ok := isCreateLogShardCmd(cmd); ok {
return s.handleCreateLogShardCmd(cmd)
} else if isDNHeartbeatCmd(cmd) {
return s.handleDNHeartbeat(cmd)
} else if isLogHeartbeatCmd(cmd) {
return s.handleLogHeartbeat(cmd)
} else if isTickCmd(cmd) {
return s.handleTick(cmd)
}
panic(moerr.NewError(moerr.INVALID_INPUT, "unexpected haKeeper cmd"))
}

func (s *stateMachine) Lookup(query interface{}) (interface{}, error) {
if q, ok := query.(*logShardIDQuery); ok {
id, ok := s.LogShards[q.name]
if ok {
return &logShardIDQueryResult{found: true, id: id}, nil
}
return &logShardIDQueryResult{found: false}, nil
}
panic("unknown query type")
}

func (s *stateMachine) SaveSnapshot(w io.Writer,
_ sm.ISnapshotFileCollection, _ <-chan struct{}) error {
enc := gob.NewEncoder(w)
return enc.Encode(s)
}

func (s *stateMachine) RecoverFromSnapshot(r io.Reader,
_ []sm.SnapshotFile, _ <-chan struct{}) error {
dec := gob.NewDecoder(r)
return dec.Decode(s)
}
Loading

0 comments on commit fa18348

Please sign in to comment.