From 4129cee4e4e73ef14916765b00ba11c50f745f42 Mon Sep 17 00:00:00 2001 From: Wolfgang Welz <welzwo@gmail.com> Date: Mon, 25 May 2020 15:39:34 +0200 Subject: [PATCH] Upgrade to latest hive.go version (#433) * adapt to new hive.go version * upgrade hive.go * use store backed sequence * add option to use in-memory database * address review comments --- dapps/valuetransfers/dapp.go | 4 +- .../packages/branchmanager/branchmanager.go | 6 +- .../branchmanager/branchmanager_test.go | 4 +- .../valuetransfers/packages/tangle/tangle.go | 8 +- .../packages/test/valuetransfers_test.go | 4 +- go.mod | 2 +- go.sum | 7 +- .../messagefactory/messagefactory.go | 13 +-- .../messagefactory/messagefactory_test.go | 22 +---- packages/binary/messagelayer/tangle/tangle.go | 6 +- .../binary/messagelayer/tangle/tangle_test.go | 27 +---- .../binary/messagelayer/test/message_test.go | 11 ++- packages/binary/testutil/db.go | 36 ------- packages/binary/testutil/messagefactory.go | 29 ------ packages/database/badgerdb.go | 98 ++++++++++++++++++ packages/database/database.go | 99 +++---------------- packages/database/memdb.go | 32 ++++++ packages/database/parameters.go | 13 --- .../{prefixes.go => prefix/prefix.go} | 2 +- packages/database/versioning.go | 47 --------- packages/gossip/manager_test.go | 2 +- packages/gossip/server/server_test.go | 2 +- plugins/autopeering/autopeering.go | 3 +- plugins/autopeering/local/local.go | 9 +- plugins/database/parameters.go | 17 ++++ plugins/database/plugin.go | 90 ++++++++++++++--- plugins/database/versioning.go | 41 ++++++++ plugins/messagelayer/plugin.go | 7 +- tools/integration-tests/tester/go.mod | 2 +- tools/integration-tests/tester/go.sum | 7 +- 30 files changed, 339 insertions(+), 311 deletions(-) delete mode 100644 packages/binary/testutil/db.go delete mode 100644 packages/binary/testutil/messagefactory.go create mode 100644 packages/database/badgerdb.go create mode 100644 packages/database/memdb.go delete mode 100644 packages/database/parameters.go rename packages/database/{prefixes.go => prefix/prefix.go} (91%) delete mode 100644 packages/database/versioning.go create mode 100644 plugins/database/parameters.go create mode 100644 plugins/database/versioning.go diff --git a/dapps/valuetransfers/dapp.go b/dapps/valuetransfers/dapp.go index 1fd95f1e..ab27ed9d 100644 --- a/dapps/valuetransfers/dapp.go +++ b/dapps/valuetransfers/dapp.go @@ -3,6 +3,7 @@ package valuetransfers import ( "time" + "github.com/iotaledger/goshimmer/plugins/database" "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" @@ -14,7 +15,6 @@ import ( "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" messageTangle "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" - "github.com/iotaledger/goshimmer/packages/database" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/vote" "github.com/iotaledger/goshimmer/plugins/messagelayer" @@ -48,7 +48,7 @@ func configure(_ *node.Plugin) { log.Debug("configuring ValueTransfers") // create instances - Tangle = tangle.New(database.GetBadgerInstance()) + Tangle = tangle.New(database.Store()) // subscribe to message-layer messagelayer.Tangle.Events.MessageSolid.Attach(events.NewClosure(onReceiveMessageFromMessageLayer)) diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager.go b/dapps/valuetransfers/packages/branchmanager/branchmanager.go index ecade28a..5342c9ef 100644 --- a/dapps/valuetransfers/packages/branchmanager/branchmanager.go +++ b/dapps/valuetransfers/packages/branchmanager/branchmanager.go @@ -5,7 +5,7 @@ import ( "fmt" "sort" - "github.com/dgraph-io/badger/v2" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/marshalutil" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/types" @@ -39,8 +39,8 @@ type BranchManager struct { } // New is the constructor of the BranchManager. -func New(badgerInstance *badger.DB) (branchManager *BranchManager) { - osFactory := objectstorage.NewFactory(badgerInstance, storageprefix.ValueTransfers) +func New(store kvstore.KVStore) (branchManager *BranchManager) { + osFactory := objectstorage.NewFactory(store, storageprefix.ValueTransfers) branchManager = &BranchManager{ branchStorage: osFactory.New(osBranch, osBranchFactory, osBranchOptions...), diff --git a/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go b/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go index f868df81..776e6201 100644 --- a/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go +++ b/dapps/valuetransfers/packages/branchmanager/branchmanager_test.go @@ -5,11 +5,11 @@ import ( "testing" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" - "github.com/iotaledger/goshimmer/packages/binary/testutil" + "github.com/iotaledger/hive.go/kvstore/mapdb" ) func TestSomething(t *testing.T) { - branchManager := New(testutil.DB(t)) + branchManager := New(mapdb.NewMapDB()) cachedBranch1, _ := branchManager.Fork(BranchID{2}, []BranchID{MasterBranchID}, []ConflictID{transaction.OutputID{4}}) defer cachedBranch1.Release() diff --git a/dapps/valuetransfers/packages/tangle/tangle.go b/dapps/valuetransfers/packages/tangle/tangle.go index 22794487..ac495ff9 100644 --- a/dapps/valuetransfers/packages/tangle/tangle.go +++ b/dapps/valuetransfers/packages/tangle/tangle.go @@ -7,8 +7,8 @@ import ( "math" "time" - "github.com/dgraph-io/badger/v2" "github.com/iotaledger/hive.go/async" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/objectstorage" "github.com/iotaledger/hive.go/types" @@ -42,11 +42,11 @@ type Tangle struct { } // New is the constructor of a Tangle and creates a new Tangle object from the given details. -func New(badgerInstance *badger.DB) (result *Tangle) { - osFactory := objectstorage.NewFactory(badgerInstance, storageprefix.ValueTransfers) +func New(store kvstore.KVStore) (result *Tangle) { + osFactory := objectstorage.NewFactory(store, storageprefix.ValueTransfers) result = &Tangle{ - branchManager: branchmanager.New(badgerInstance), + branchManager: branchmanager.New(store), payloadStorage: osFactory.New(osPayload, osPayloadFactory, objectstorage.CacheTime(time.Second)), payloadMetadataStorage: osFactory.New(osPayloadMetadata, osPayloadMetadataFactory, objectstorage.CacheTime(time.Second)), diff --git a/dapps/valuetransfers/packages/test/valuetransfers_test.go b/dapps/valuetransfers/packages/test/valuetransfers_test.go index d321c931..aed10000 100644 --- a/dapps/valuetransfers/packages/test/valuetransfers_test.go +++ b/dapps/valuetransfers/packages/test/valuetransfers_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/iotaledger/hive.go/crypto/ed25519" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/stretchr/testify/assert" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/address" @@ -12,12 +13,11 @@ import ( "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/payload" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/tangle" "github.com/iotaledger/goshimmer/dapps/valuetransfers/packages/transaction" - "github.com/iotaledger/goshimmer/packages/binary/testutil" ) func TestTangle_ValueTransfer(t *testing.T) { // initialize tangle + ledgerstate - valueTangle := tangle.New(testutil.DB(t)) + valueTangle := tangle.New(mapdb.NewMapDB()) if err := valueTangle.Prune(); err != nil { t.Error(err) diff --git a/go.mod b/go.mod index d2f4b155..eef91d29 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/gobuffalo/packr/v2 v2.7.1 github.com/golang/protobuf v1.3.5 github.com/gorilla/websocket v1.4.1 - github.com/iotaledger/hive.go v0.0.0-20200513180357-f0ac8c45b754 + github.com/iotaledger/hive.go v0.0.0-20200522163415-89e97bbc3106 github.com/iotaledger/iota.go v1.0.0-beta.14 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 diff --git a/go.sum b/go.sum index bc4b4143..40b423a8 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200513180357-f0ac8c45b754 h1:UCyAisLvAuKIWf2bMz+iYYgjGdHS7H4W2wMTpWg9yl8= -github.com/iotaledger/hive.go v0.0.0-20200513180357-f0ac8c45b754/go.mod h1:HgYsLMzyQV+eaiUrxa1c7qvH9Jwi2ncycqtlw+Lczhs= +github.com/iotaledger/hive.go v0.0.0-20200522163415-89e97bbc3106 h1:oUQBCk4NmAohCzpI2md15qGeSp0RwKIOdNVWIM5N3hs= +github.com/iotaledger/hive.go v0.0.0-20200522163415-89e97bbc3106/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= @@ -329,6 +329,8 @@ go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYr go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.mongodb.org/mongo-driver v1.0.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -415,6 +417,7 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200427175716-29b57079015a h1:08u6b1caTT9MQY4wSbmsd4Ulm6DmgNYnbImBuZjGJow= golang.org/x/sys v0.0.0-20200427175716-29b57079015a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/packages/binary/messagelayer/messagefactory/messagefactory.go b/packages/binary/messagelayer/messagefactory/messagefactory.go index 331bacb2..7f06f292 100644 --- a/packages/binary/messagelayer/messagefactory/messagefactory.go +++ b/packages/binary/messagelayer/messagefactory/messagefactory.go @@ -4,25 +4,26 @@ import ( "fmt" "time" - "github.com/dgraph-io/badger/v2" - "github.com/iotaledger/hive.go/identity" - "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" + "github.com/iotaledger/hive.go/identity" + "github.com/iotaledger/hive.go/kvstore" ) +const storeSequenceInterval = 100 + // MessageFactory acts as a factory to create new messages. type MessageFactory struct { Events *Events - sequence *badger.Sequence + sequence *kvstore.Sequence localIdentity *identity.LocalIdentity tipSelector *tipselector.TipSelector } // New creates a new message factory. -func New(db *badger.DB, localIdentity *identity.LocalIdentity, tipSelector *tipselector.TipSelector, sequenceKey []byte) *MessageFactory { - sequence, err := db.GetSequence(sequenceKey, 100) +func New(store kvstore.KVStore, localIdentity *identity.LocalIdentity, tipSelector *tipselector.TipSelector, sequenceKey []byte) *MessageFactory { + sequence, err := kvstore.NewSequence(store, sequenceKey, storeSequenceInterval) if err != nil { panic(fmt.Sprintf("could not create message sequence number: %v", err)) } diff --git a/packages/binary/messagelayer/messagefactory/messagefactory_test.go b/packages/binary/messagelayer/messagefactory/messagefactory_test.go index 5c8466cc..f54aecb4 100644 --- a/packages/binary/messagelayer/messagefactory/messagefactory_test.go +++ b/packages/binary/messagelayer/messagefactory/messagefactory_test.go @@ -2,8 +2,6 @@ package messagefactory import ( "encoding" - "io/ioutil" - "os" "sync" "sync/atomic" "testing" @@ -12,13 +10,11 @@ import ( "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" - "github.com/iotaledger/goshimmer/packages/database" - "github.com/iotaledger/goshimmer/plugins/config" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/identity" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) const ( @@ -27,22 +23,12 @@ const ( ) func TestMessageFactory_BuildMessage(t *testing.T) { - // Set up DB for testing - dir, err := ioutil.TempDir("", t.Name()) - require.NoError(t, err) - defer os.Remove(dir) - // use the tempdir for the database - config.Node.Set(database.CFG_DIRECTORY, dir) - db := database.GetBadgerInstance() - - localIdentity := identity.GenerateLocalIdentity() - tipSelector := tipselector.New() + msgFactory := New(mapdb.NewMapDB(), identity.GenerateLocalIdentity(), tipselector.New(), []byte(sequenceKey)) + defer msgFactory.Shutdown() // keep track of sequence numbers sequenceNumbers := sync.Map{} - msgFactory := New(db, localIdentity, tipSelector, []byte(sequenceKey)) - // attach to event and count countEvents := uint64(0) msgFactory.Events.MessageConstructed.Attach(events.NewClosure(func(msg *message.Message) { @@ -116,8 +102,6 @@ func TestMessageFactory_BuildMessage(t *testing.T) { }) assert.EqualValues(t, totalMessages-1, max) assert.EqualValues(t, totalMessages, countSequence) - - _ = db.Close() } type MockPayload struct { diff --git a/packages/binary/messagelayer/tangle/tangle.go b/packages/binary/messagelayer/tangle/tangle.go index 702963c2..9a5f0215 100644 --- a/packages/binary/messagelayer/tangle/tangle.go +++ b/packages/binary/messagelayer/tangle/tangle.go @@ -4,7 +4,7 @@ import ( "container/list" "time" - "github.com/dgraph-io/badger/v2" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/types" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" @@ -50,8 +50,8 @@ func missingMessageFactory(key []byte) (objectstorage.StorableObject, int, error } // New creates a new Tangle. -func New(badgerInstance *badger.DB) (result *Tangle) { - osFactory := objectstorage.NewFactory(badgerInstance, storageprefix.MessageLayer) +func New(store kvstore.KVStore) (result *Tangle) { + osFactory := objectstorage.NewFactory(store, storageprefix.MessageLayer) result = &Tangle{ shutdown: make(chan struct{}), diff --git a/packages/binary/messagelayer/tangle/tangle_test.go b/packages/binary/messagelayer/tangle/tangle_test.go index 98eb4ffd..51bcb083 100644 --- a/packages/binary/messagelayer/tangle/tangle_test.go +++ b/packages/binary/messagelayer/tangle/tangle_test.go @@ -2,29 +2,18 @@ package tangle import ( "fmt" - "io/ioutil" - "os" "testing" "time" - "github.com/iotaledger/hive.go/events" - "github.com/iotaledger/hive.go/identity" - "github.com/stretchr/testify/require" - "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" - "github.com/iotaledger/goshimmer/packages/database" - "github.com/iotaledger/goshimmer/plugins/config" + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/identity" + "github.com/iotaledger/hive.go/kvstore/mapdb" ) func BenchmarkTangle_AttachMessage(b *testing.B) { - dir, err := ioutil.TempDir("", b.Name()) - require.NoError(b, err) - defer os.Remove(dir) - // use the tempdir for the database - config.Node.Set(database.CFG_DIRECTORY, dir) - - tangle := New(database.GetBadgerInstance()) + tangle := New(mapdb.NewMapDB()) if err := tangle.Prune(); err != nil { b.Error(err) @@ -49,13 +38,7 @@ func BenchmarkTangle_AttachMessage(b *testing.B) { } func TestTangle_AttachMessage(t *testing.T) { - dir, err := ioutil.TempDir("", t.Name()) - require.NoError(t, err) - defer os.Remove(dir) - // use the tempdir for the database - config.Node.Set(database.CFG_DIRECTORY, dir) - - messageTangle := New(database.GetBadgerInstance()) + messageTangle := New(mapdb.NewMapDB()) if err := messageTangle.Prune(); err != nil { t.Error(err) diff --git a/packages/binary/messagelayer/test/message_test.go b/packages/binary/messagelayer/test/message_test.go index 70684aa3..fca5ec70 100644 --- a/packages/binary/messagelayer/test/message_test.go +++ b/packages/binary/messagelayer/test/message_test.go @@ -5,11 +5,15 @@ import ( "testing" "time" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory" + "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" + "github.com/iotaledger/goshimmer/plugins/messagelayer" + "github.com/iotaledger/hive.go/identity" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/stretchr/testify/assert" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/message" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/payload" - "github.com/iotaledger/goshimmer/packages/binary/testutil" ) func TestMessage_StorableObjectFromKey(t *testing.T) { @@ -28,7 +32,10 @@ func TestMessage_StorableObjectFromKey(t *testing.T) { } func TestMessage_MarshalUnmarshal(t *testing.T) { - testMessage := testutil.MessageFactory(t).IssuePayload(payload.NewData([]byte("sth"))) + msgFactory := messagefactory.New(mapdb.NewMapDB(), identity.GenerateLocalIdentity(), tipselector.New(), []byte(messagelayer.DBSequenceNumber)) + defer msgFactory.Shutdown() + + testMessage := msgFactory.IssuePayload(payload.NewData([]byte("sth"))) assert.Equal(t, true, testMessage.VerifySignature()) fmt.Print(testMessage) diff --git a/packages/binary/testutil/db.go b/packages/binary/testutil/db.go deleted file mode 100644 index 554512a1..00000000 --- a/packages/binary/testutil/db.go +++ /dev/null @@ -1,36 +0,0 @@ -package testutil - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/dgraph-io/badger/v2" - "github.com/stretchr/testify/require" - - "github.com/iotaledger/goshimmer/packages/database" - "github.com/iotaledger/goshimmer/plugins/config" -) - -var dbInstance *badger.DB - -func DB(t *testing.T) *badger.DB { - if dbInstance == nil { - // Set up DB for testing - dir, err := ioutil.TempDir("", t.Name()) - require.NoError(t, err) - - t.Cleanup(func() { - os.Remove(dir) - - dbInstance = nil - }) - - // use the tempdir for the database - config.Node.Set(database.CFG_DIRECTORY, dir) - - dbInstance = database.GetBadgerInstance() - } - - return dbInstance -} diff --git a/packages/binary/testutil/messagefactory.go b/packages/binary/testutil/messagefactory.go deleted file mode 100644 index 093ee869..00000000 --- a/packages/binary/testutil/messagefactory.go +++ /dev/null @@ -1,29 +0,0 @@ -package testutil - -import ( - "testing" - - "github.com/iotaledger/hive.go/identity" - - "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagefactory" - "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" -) - -const sequenceKey = "seq" - -var messageFactoryInstance *messagefactory.MessageFactory - -func MessageFactory(t *testing.T) *messagefactory.MessageFactory { - if messageFactoryInstance == nil { - localIdentity := identity.GenerateLocalIdentity() - tipSelector := tipselector.New() - - t.Cleanup(func() { - messageFactoryInstance = nil - }) - - messageFactoryInstance = messagefactory.New(DB(t), localIdentity, tipSelector, []byte(sequenceKey)) - } - - return messageFactoryInstance -} diff --git a/packages/database/badgerdb.go b/packages/database/badgerdb.go new file mode 100644 index 00000000..4246add3 --- /dev/null +++ b/packages/database/badgerdb.go @@ -0,0 +1,98 @@ +package database + +import ( + "fmt" + "os" + "runtime" + + "github.com/dgraph-io/badger/v2" + "github.com/dgraph-io/badger/v2/options" + "github.com/iotaledger/hive.go/kvstore" + badgerstore "github.com/iotaledger/hive.go/kvstore/badger" +) + +const valueLogGCDiscardRatio = 0.1 + +type badgerDB struct { + *badger.DB +} + +// NewDB returns a new persisting DB object. +func NewDB(dirname string) (DB, error) { + // assure that the directory exists + err := createDir(dirname) + if err != nil { + return nil, fmt.Errorf("could not create DB directory: %w", err) + } + + opts := badger.DefaultOptions(dirname) + + opts.Logger = nil + opts.SyncWrites = false + opts.TableLoadingMode = options.MemoryMap + opts.ValueLogLoadingMode = options.MemoryMap + opts.CompactL0OnClose = false + opts.KeepL0InMemory = false + opts.VerifyValueChecksum = false + opts.ZSTDCompressionLevel = 1 + opts.Compression = options.None + opts.MaxCacheSize = 50000000 + opts.EventLogging = false + + if runtime.GOOS == "windows" { + opts = opts.WithTruncate(true) + } + + db, err := badger.Open(opts) + if err != nil { + return nil, fmt.Errorf("could not open DB: %w", err) + } + + return &badgerDB{DB: db}, nil +} + +func (db *badgerDB) NewStore() kvstore.KVStore { + return badgerstore.New(db.DB) +} + +// Close closes a DB. It's crucial to call it to ensure all the pending updates make their way to disk. +func (db *badgerDB) Close() error { + return db.DB.Close() +} + +func (db *badgerDB) RequiresGC() bool { + return true +} + +func (db *badgerDB) GC() error { + err := db.RunValueLogGC(valueLogGCDiscardRatio) + if err != nil { + return err + } + // trigger the go garbage collector to release the used memory + runtime.GC() + return nil +} + +// Returns whether the given file or directory exists. +func exists(path string) (bool, error) { + _, err := os.Stat(path) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, err +} + +func createDir(dirname string) error { + exists, err := exists(dirname) + if err != nil { + return err + } + if !exists { + return os.Mkdir(dirname, 0700) + } + return nil +} diff --git a/packages/database/database.go b/packages/database/database.go index 809ef459..200cc67c 100644 --- a/packages/database/database.go +++ b/packages/database/database.go @@ -1,93 +1,18 @@ -// Wrapper for hive.go/database package. Only use this instead of the hive.go package. package database import ( - "io/ioutil" - "os" - "runtime" - "sync" - - "github.com/dgraph-io/badger/v2" - "github.com/dgraph-io/badger/v2/options" - "github.com/iotaledger/hive.go/database" - "github.com/iotaledger/hive.go/logger" - - "github.com/iotaledger/goshimmer/plugins/config" -) - -var ( - instance *badger.DB - once sync.Once - ErrKeyNotFound = database.ErrKeyNotFound + "github.com/iotaledger/hive.go/kvstore" ) -type ( - Database = database.Database - Entry = database.Entry - KeyOnlyEntry = database.KeyOnlyEntry - KeyPrefix = database.KeyPrefix - Key = database.Key - Value = database.Value -) - -func Get(dbPrefix byte, optionalBadger ...*badger.DB) (Database, error) { - return database.Get(dbPrefix, optionalBadger...) -} - -func GetBadgerInstance() *badger.DB { - once.Do(func() { - dbDir := config.Node.GetString(CFG_DIRECTORY) - - var dbDirClear bool - // check whether the database is new, by checking whether any file exists within - // the database directory - fileInfos, err := ioutil.ReadDir(dbDir) - if err != nil { - // panic on other errors, for example permission related - if !os.IsNotExist(err) { - panic(err) - } - dbDirClear = true - } - if len(fileInfos) == 0 { - dbDirClear = true - } - - opts := badger.DefaultOptions(dbDir) - opts.Logger = nil - if runtime.GOOS == "windows" { - opts = opts.WithTruncate(true) - } - - opts.SyncWrites = false - opts.TableLoadingMode = options.MemoryMap - opts.ValueLogLoadingMode = options.MemoryMap - opts.CompactL0OnClose = false - opts.KeepL0InMemory = false - opts.VerifyValueChecksum = false - opts.ZSTDCompressionLevel = 1 - opts.Compression = options.None - opts.MaxCacheSize = 50000000 - opts.EventLogging = false - - db, err := database.CreateDB(dbDir, opts) - if err != nil { - // errors should cause a panic to avoid singleton deadlocks - panic(err) - } - instance = db - - // up on the first caller, check whether the version of the database is compatible - checkDatabaseVersion(dbDirClear) - }) - return instance -} - -func CleanupBadgerInstance(log *logger.Logger) { - db := GetBadgerInstance() - log.Info("Running Badger database garbage collection") - var err error - for err == nil { - err = db.RunValueLogGC(0.7) - } +// DB represents a database abstraction. +type DB interface { + // NewStore creates a new KVStore backed by the database. + NewStore() kvstore.KVStore + // Close closes a DB. + Close() error + + // RequiresGC returns whether the database requires a call of GC() to clean deleted items. + RequiresGC() bool + // GC runs the garbage collection to clean deleted database items. + GC() error } diff --git a/packages/database/memdb.go b/packages/database/memdb.go new file mode 100644 index 00000000..2ff8d979 --- /dev/null +++ b/packages/database/memdb.go @@ -0,0 +1,32 @@ +package database + +import ( + "github.com/iotaledger/hive.go/kvstore" + "github.com/iotaledger/hive.go/kvstore/mapdb" +) + +type memDB struct { + *mapdb.MapDB +} + +// NewMemDB returns a new in-memory (not persisted) DB object. +func NewMemDB() (DB, error) { + return &memDB{MapDB: mapdb.NewMapDB()}, nil +} + +func (db *memDB) NewStore() kvstore.KVStore { + return db.MapDB +} + +func (db *memDB) Close() error { + db.MapDB = nil + return nil +} + +func (db *memDB) RequiresGC() bool { + return false +} + +func (db *memDB) GC() error { + return nil +} diff --git a/packages/database/parameters.go b/packages/database/parameters.go deleted file mode 100644 index 8d647e65..00000000 --- a/packages/database/parameters.go +++ /dev/null @@ -1,13 +0,0 @@ -package database - -import ( - flag "github.com/spf13/pflag" -) - -const ( - CFG_DIRECTORY = "database.directory" -) - -func init() { - flag.String(CFG_DIRECTORY, "mainnetdb", "path to the database folder") -} diff --git a/packages/database/prefixes.go b/packages/database/prefix/prefix.go similarity index 91% rename from packages/database/prefixes.go rename to packages/database/prefix/prefix.go index c9dc488d..005c6dfa 100644 --- a/packages/database/prefixes.go +++ b/packages/database/prefix/prefix.go @@ -1,4 +1,4 @@ -package database +package prefix const ( DBPrefixApprovers byte = iota diff --git a/packages/database/versioning.go b/packages/database/versioning.go deleted file mode 100644 index 5150bc62..00000000 --- a/packages/database/versioning.go +++ /dev/null @@ -1,47 +0,0 @@ -package database - -import ( - "errors" - "fmt" -) - -const ( - // DBVersion defines the version of the database schema this version of GoShimmer supports. - // everytime there's a breaking change regarding the stored data, this version flag should be adjusted. - DBVersion = 2 -) - -var ( - ErrDBVersionIncompatible = errors.New("database version is not compatible. please delete your database folder and restart") - // the key under which the database is stored - dbVersionKey = []byte{0} -) - -// checks whether the database is compatible with the current schema version. -// also automatically sets the version if the database is new. -func checkDatabaseVersion(dbIsNew bool) { - dbInstance, err := Get(DBPrefixDatabaseVersion, instance) - if err != nil { - panic(err) - } - - if dbIsNew { - // store db version for the first time in the new database - if err = dbInstance.Set(Entry{Key: dbVersionKey, Value: []byte{DBVersion}}); err != nil { - panic(fmt.Sprintf("unable to persist db version number: %s", err.Error())) - } - return - } - - // db version must be available - entry, err := dbInstance.Get(dbVersionKey) - if err != nil { - if err == ErrKeyNotFound { - panic(err) - } - panic(fmt.Errorf("%w: no database version was persisted", ErrDBVersionIncompatible)) - } - if entry.Value[0] != DBVersion { - panic(fmt.Errorf("%w: supported version: %d, version of database: %d", ErrDBVersionIncompatible, DBVersion, entry.Value[0])) - } -} diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go index 399fa175..7dcc2d9e 100644 --- a/packages/gossip/manager_test.go +++ b/packages/gossip/manager_test.go @@ -12,8 +12,8 @@ import ( "github.com/iotaledger/goshimmer/packages/gossip/server" "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/autopeering/peer/service" - "github.com/iotaledger/hive.go/database/mapdb" "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/logger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/packages/gossip/server/server_test.go b/packages/gossip/server/server_test.go index 5c62ef5e..7fa8defa 100644 --- a/packages/gossip/server/server_test.go +++ b/packages/gossip/server/server_test.go @@ -8,7 +8,7 @@ import ( "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/autopeering/peer/service" - "github.com/iotaledger/hive.go/database/mapdb" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/logger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go index 101b53dc..d9f60b6d 100644 --- a/plugins/autopeering/autopeering.go +++ b/plugins/autopeering/autopeering.go @@ -159,8 +159,7 @@ func start(shutdownSignal <-chan struct{}) { Discovery().Close() Selection().Close() - // persists potential peering seeds for the next start - log.Infof("%d peers persisted as seeds", lPeer.Database().PersistSeeds()) + lPeer.Database().Close() } func hash32(b []byte) uint32 { diff --git a/plugins/autopeering/local/local.go b/plugins/autopeering/local/local.go index c16f755e..040dcc0f 100644 --- a/plugins/autopeering/local/local.go +++ b/plugins/autopeering/local/local.go @@ -7,8 +7,9 @@ import ( "strings" "sync" - "github.com/iotaledger/goshimmer/packages/database" + "github.com/iotaledger/goshimmer/packages/database/prefix" "github.com/iotaledger/goshimmer/plugins/config" + "github.com/iotaledger/goshimmer/plugins/database" "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/logger" @@ -57,11 +58,7 @@ func configureLocal() *peer.Local { } seed = append(seed, bytes) } - badgerDB, err := database.Get(database.DBPrefixAutoPeering, database.GetBadgerInstance()) - if err != nil { - log.Fatalf("Error loading DB: %s", err) - } - peerDB, err := peer.NewDB(badgerDB) + peerDB, err := peer.NewDB(database.StoreRealm([]byte{prefix.DBPrefixAutoPeering})) if err != nil { log.Fatalf("Error creating peer DB: %s", err) } diff --git a/plugins/database/parameters.go b/plugins/database/parameters.go new file mode 100644 index 00000000..c860dd8b --- /dev/null +++ b/plugins/database/parameters.go @@ -0,0 +1,17 @@ +package database + +import ( + flag "github.com/spf13/pflag" +) + +const ( + // CfgDatabaseDir defines the directory of the database. + CfgDatabaseDir = "database.directory" + // CfgDatabaseInMemory defines whether to use an in-memory database. + CfgDatabaseInMemory = "database.inMemory" +) + +func init() { + flag.String(CfgDatabaseDir, "mainnetdb", "path to the database folder") + flag.Bool(CfgDatabaseInMemory, false, "whether the database is only kept in memory and not persisted") +} diff --git a/plugins/database/plugin.go b/plugins/database/plugin.go index 6a006b7b..99333abf 100644 --- a/plugins/database/plugin.go +++ b/plugins/database/plugin.go @@ -2,11 +2,16 @@ package database import ( + "errors" + "sync" "time" "github.com/iotaledger/goshimmer/packages/database" + "github.com/iotaledger/goshimmer/packages/database/prefix" "github.com/iotaledger/goshimmer/packages/shutdown" + "github.com/iotaledger/goshimmer/plugins/config" "github.com/iotaledger/hive.go/daemon" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" "github.com/iotaledger/hive.go/timeutil" @@ -19,25 +24,82 @@ var ( // Plugin is the plugin instance of the database plugin. Plugin = node.NewPlugin(PluginName, node.Enabled, configure, run) log *logger.Logger + + db database.DB + store kvstore.KVStore + storeOnce sync.Once ) -func configure(_ *node.Plugin) { +// Store returns the KVStore instance. +func Store() kvstore.KVStore { + storeOnce.Do(createStore) + return store +} + +// StoreRealm is a factory method for a different realm backed by the KVStore instance. +func StoreRealm(realm kvstore.Realm) kvstore.KVStore { + return Store().WithRealm(realm) +} + +func createStore() { log = logger.NewLogger(PluginName) - _ = database.GetBadgerInstance() + var err error + if config.Node.GetBool(CfgDatabaseInMemory) { + db, err = database.NewMemDB() + } else { + dbDir := config.Node.GetString(CfgDatabaseDir) + db, err = database.NewDB(dbDir) + } + if err != nil { + log.Fatal(err) + } + + store = db.NewStore() +} + +func configure(_ *node.Plugin) { + // assure that the store is initialized + store := Store() + + err := checkDatabaseVersion(store.WithRealm([]byte{prefix.DBPrefixDatabaseVersion})) + if errors.Is(err, ErrDBVersionIncompatible) { + log.Panicf("The database scheme was updated. Please delete the database folder.\n%s", err) + } + if err != nil { + log.Panicf("Failed to check database version: %s", err) + } + + // we open the database in the configure, so we must also make sure it's closed here + err = daemon.BackgroundWorker(PluginName, closeDB, shutdown.PriorityDatabase) + if err != nil { + log.Panicf("Failed to start as daemon: %s", err) + } } func run(_ *node.Plugin) { - daemon.BackgroundWorker(PluginName+"_GC", func(shutdownSignal <-chan struct{}) { - timeutil.Ticker(func() { - database.CleanupBadgerInstance(log) - }, 5*time.Minute, shutdownSignal) - }, shutdown.PriorityBadgerGarbageCollection) - - daemon.BackgroundWorker(PluginName, func(shutdownSignal <-chan struct{}) { - <-shutdownSignal - log.Infof("Syncing database to disk...") - database.GetBadgerInstance().Close() - log.Infof("Syncing database to disk... done") - }, shutdown.PriorityDatabase) + if err := daemon.BackgroundWorker(PluginName+"[GC]", runGC, shutdown.PriorityBadgerGarbageCollection); err != nil { + log.Errorf("Failed to start as daemon: %s", err) + } +} + +func closeDB(shutdownSignal <-chan struct{}) { + <-shutdownSignal + log.Infof("Syncing database to disk...") + if err := db.Close(); err != nil { + log.Errorf("Failed to flush the database: %s", err) + } + log.Infof("Syncing database to disk... done") +} + +func runGC(shutdownSignal <-chan struct{}) { + if !db.RequiresGC() { + return + } + // run the garbage collection with the given interval + timeutil.Ticker(func() { + if err := db.GC(); err != nil { + log.Warnf("Garbage collection failed: %s", err) + } + }, 5*time.Minute, shutdownSignal) } diff --git a/plugins/database/versioning.go b/plugins/database/versioning.go new file mode 100644 index 00000000..0ee20f7b --- /dev/null +++ b/plugins/database/versioning.go @@ -0,0 +1,41 @@ +package database + +import ( + "errors" + "fmt" + + "github.com/iotaledger/hive.go/kvstore" +) + +const ( + // DBVersion defines the version of the database schema this version of GoShimmer supports. + // Every time there's a breaking change regarding the stored data, this version flag should be adjusted. + DBVersion = 2 +) + +var ( + // ErrDBVersionIncompatible is returned when the database has an unexpected version. + ErrDBVersionIncompatible = errors.New("database version is not compatible. please delete your database folder and restart") + // the key under which the database is stored + dbVersionKey = []byte{0} +) + +// checks whether the database is compatible with the current schema version. +// also automatically sets the version if the database is new. +func checkDatabaseVersion(store kvstore.KVStore) error { + entry, err := store.Get(dbVersionKey) + if err == kvstore.ErrKeyNotFound { + // set the version in an empty DB + return store.Set(dbVersionKey, []byte{DBVersion}) + } + if err != nil { + return err + } + if len(entry) == 0 { + return fmt.Errorf("%w: no database version was persisted", ErrDBVersionIncompatible) + } + if entry[0] != DBVersion { + return fmt.Errorf("%w: supported version: %d, version of database: %d", ErrDBVersionIncompatible, DBVersion, entry[0]) + } + return nil +} diff --git a/plugins/messagelayer/plugin.go b/plugins/messagelayer/plugin.go index 9541eff4..e5088a86 100644 --- a/plugins/messagelayer/plugin.go +++ b/plugins/messagelayer/plugin.go @@ -7,9 +7,9 @@ import ( "github.com/iotaledger/goshimmer/packages/binary/messagelayer/messagerequester" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tangle" "github.com/iotaledger/goshimmer/packages/binary/messagelayer/tipselector" - "github.com/iotaledger/goshimmer/packages/database" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/plugins/autopeering/local" + "github.com/iotaledger/goshimmer/plugins/database" "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/events" @@ -35,15 +35,16 @@ var ( func configure(*node.Plugin) { log = logger.NewLogger(PluginName) + store := database.Store() // create instances MessageParser = messageparser.New() MessageRequester = messagerequester.New() TipSelector = tipselector.New() - Tangle = tangle.New(database.GetBadgerInstance()) + Tangle = tangle.New(store) // Setup MessageFactory (behavior + logging)) - MessageFactory = messagefactory.New(database.GetBadgerInstance(), local.GetInstance().LocalIdentity(), TipSelector, []byte(DBSequenceNumber)) + MessageFactory = messagefactory.New(database.Store(), local.GetInstance().LocalIdentity(), TipSelector, []byte(DBSequenceNumber)) MessageFactory.Events.MessageConstructed.Attach(events.NewClosure(Tangle.AttachMessage)) MessageFactory.Events.Error.Attach(events.NewClosure(func(err error) { log.Errorf("internal error in message factory: %v", err) diff --git a/tools/integration-tests/tester/go.mod b/tools/integration-tests/tester/go.mod index 8f31861c..7142d70f 100644 --- a/tools/integration-tests/tester/go.mod +++ b/tools/integration-tests/tester/go.mod @@ -10,7 +10,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/drand/drand v0.8.1 github.com/iotaledger/goshimmer v0.1.3 - github.com/iotaledger/hive.go v0.0.0-20200513180357-f0ac8c45b754 + github.com/iotaledger/hive.go v0.0.0-20200522163415-89e97bbc3106 github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/stretchr/testify v1.5.1 ) diff --git a/tools/integration-tests/tester/go.sum b/tools/integration-tests/tester/go.sum index f71c038d..f9bb0545 100644 --- a/tools/integration-tests/tester/go.sum +++ b/tools/integration-tests/tester/go.sum @@ -138,8 +138,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200513180357-f0ac8c45b754 h1:UCyAisLvAuKIWf2bMz+iYYgjGdHS7H4W2wMTpWg9yl8= -github.com/iotaledger/hive.go v0.0.0-20200513180357-f0ac8c45b754/go.mod h1:HgYsLMzyQV+eaiUrxa1c7qvH9Jwi2ncycqtlw+Lczhs= +github.com/iotaledger/hive.go v0.0.0-20200522163415-89e97bbc3106 h1:oUQBCk4NmAohCzpI2md15qGeSp0RwKIOdNVWIM5N3hs= +github.com/iotaledger/hive.go v0.0.0-20200522163415-89e97bbc3106/go.mod h1:zwZhaE4ZeglpTrbmbwdnVPMI5XdRu2RmByi3Qn0ztmU= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= @@ -318,6 +318,8 @@ go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYr go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.mongodb.org/mongo-driver v1.0.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -406,6 +408,7 @@ golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200427175716-29b57079015a h1:08u6b1caTT9MQY4wSbmsd4Ulm6DmgNYnbImBuZjGJow= golang.org/x/sys v0.0.0-20200427175716-29b57079015a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -- GitLab