mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-24 03:09:22 -06:00
Make DataStore an interface
This commit is contained in:
@@ -33,33 +33,3 @@ type ChunkSink interface {
|
||||
Put(c Chunk)
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// NewFlags creates a new instance of Flags, which declares a number of ChunkStore-related command-line flags using the golang flag package. Call this before flag.Parse().
|
||||
func NewFlags() Flags {
|
||||
return NewFlagsWithPrefix("")
|
||||
}
|
||||
|
||||
// NewFlagsWithPrefix creates a new instance of Flags with the names of all flags declared therein prefixed by the given string.
|
||||
func NewFlagsWithPrefix(prefix string) Flags {
|
||||
return Flags{
|
||||
levelDBFlags(prefix),
|
||||
memoryFlags(prefix),
|
||||
nopFlags(prefix),
|
||||
}
|
||||
}
|
||||
|
||||
// Flags abstracts away definitions for and handling of command-line flags for all ChunkStore implementations.
|
||||
type Flags struct {
|
||||
ldb ldbStoreFlags
|
||||
memory memoryStoreFlags
|
||||
nop nopStoreFlags
|
||||
}
|
||||
|
||||
// CreateStore creates a ChunkStore implementation based on the values of command-line flags.
|
||||
func (f Flags) CreateStore() (cs ChunkStore) {
|
||||
if cs = f.ldb.createStore(); cs != nil {
|
||||
} else if cs = f.memory.createStore(); cs != nil {
|
||||
} else if cs = f.nop.createStore(); cs != nil {
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
@@ -96,19 +96,19 @@ func (l *LevelDBStore) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ldbStoreFlags struct {
|
||||
type LevelDBStoreFlags struct {
|
||||
dir *string
|
||||
maxFileHandles *int
|
||||
}
|
||||
|
||||
func levelDBFlags(prefix string) ldbStoreFlags {
|
||||
return ldbStoreFlags{
|
||||
func LevelDBFlags(prefix string) LevelDBStoreFlags {
|
||||
return LevelDBStoreFlags{
|
||||
flag.String(prefix+"ldb", "", "directory to use for a LevelDB-backed chunkstore"),
|
||||
flag.Int(prefix+"ldb-max-file-handles", 24, "max number of open file handles"),
|
||||
}
|
||||
}
|
||||
|
||||
func (f ldbStoreFlags) createStore() ChunkStore {
|
||||
func (f LevelDBStoreFlags) CreateStore() ChunkStore {
|
||||
if *f.dir == "" {
|
||||
return nil
|
||||
} else {
|
||||
|
||||
@@ -62,17 +62,17 @@ func (l *MemoryStore) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type memoryStoreFlags struct {
|
||||
type MemoryStoreFlags struct {
|
||||
use *bool
|
||||
}
|
||||
|
||||
func memoryFlags(prefix string) memoryStoreFlags {
|
||||
return memoryStoreFlags{
|
||||
func MemoryFlags(prefix string) MemoryStoreFlags {
|
||||
return MemoryStoreFlags{
|
||||
flag.Bool(prefix+"mem", false, "use a memory-based (ephemeral, and private to this application) chunkstore"),
|
||||
}
|
||||
}
|
||||
|
||||
func (f memoryStoreFlags) createStore() ChunkStore {
|
||||
func (f MemoryStoreFlags) CreateStore() ChunkStore {
|
||||
if *f.use {
|
||||
return NewMemoryStore()
|
||||
} else {
|
||||
|
||||
@@ -30,17 +30,17 @@ func (ms *NopStore) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type nopStoreFlags struct {
|
||||
type NopStoreFlags struct {
|
||||
use *bool
|
||||
}
|
||||
|
||||
func nopFlags(prefix string) nopStoreFlags {
|
||||
return nopStoreFlags{
|
||||
func NopFlags(prefix string) NopStoreFlags {
|
||||
return NopStoreFlags{
|
||||
flag.Bool(prefix+"nop", false, "use a /dev/null-esque chunkstore"),
|
||||
}
|
||||
}
|
||||
|
||||
func (f nopStoreFlags) createStore() ChunkStore {
|
||||
func (f NopStoreFlags) CreateStore() ChunkStore {
|
||||
if *f.use {
|
||||
return &NopStore{}
|
||||
} else {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/attic-labs/noms/chunks"
|
||||
"github.com/attic-labs/noms/clients/util"
|
||||
"github.com/attic-labs/noms/d"
|
||||
"github.com/attic-labs/noms/datas"
|
||||
"github.com/attic-labs/noms/http"
|
||||
)
|
||||
|
||||
@@ -17,15 +17,15 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
flags := chunks.NewFlags()
|
||||
flags := datas.NewFlags()
|
||||
flag.Parse()
|
||||
cs := flags.CreateStore()
|
||||
if cs == nil {
|
||||
ds, ok := flags.CreateDataStore()
|
||||
if !ok {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
server := http.NewHttpServer(cs, *port)
|
||||
server := http.NewHttpServer(ds, *port)
|
||||
|
||||
// Shutdown server gracefully so that profile may be written
|
||||
c := make(chan os.Signal, 1)
|
||||
@@ -34,7 +34,7 @@ func main() {
|
||||
go func() {
|
||||
<-c
|
||||
server.Stop()
|
||||
cs.Close()
|
||||
ds.Close()
|
||||
}()
|
||||
|
||||
d.Try(func() {
|
||||
|
||||
@@ -2,118 +2,34 @@ package datas
|
||||
|
||||
import (
|
||||
"github.com/attic-labs/noms/chunks"
|
||||
"github.com/attic-labs/noms/d"
|
||||
"github.com/attic-labs/noms/http"
|
||||
"github.com/attic-labs/noms/ref"
|
||||
"github.com/attic-labs/noms/types"
|
||||
)
|
||||
|
||||
// DataStore provides versioned storage for noms values. Each DataStore instance represents one moment in history. Heads() returns the Commit from each active fork at that moment. The Commit() method returns a new DataStore, representing a new moment in history.
|
||||
type DataStore struct {
|
||||
type DataStore interface {
|
||||
chunks.ChunkStore
|
||||
head *Commit
|
||||
|
||||
// MaybeHead returns the current Head Commit of this Datastore, which contains the current root of the DataStore's value tree, if available. If not, it returns a new Commit and 'false'.
|
||||
MaybeHead() (Commit, bool)
|
||||
|
||||
// Head returns the current head Commit, which contains the current root of the DataStore's value tree.
|
||||
Head() Commit
|
||||
|
||||
// Commit updates the commit that a datastore points at. The new Commit is constructed using v and the current Head. If the update cannot be performed, e.g., because of a conflict, Commit returns 'false'. The newest snapshot of the datastore is always returned.
|
||||
Commit(v types.Value) (DataStore, bool)
|
||||
|
||||
// CommitWithParents updates the commit that a datastore points at. The new Commit is constructed using v and p. If the update cannot be performed, e.g., because of a conflict, CommitWithParents returns 'false'. The newest snapshot of the datastore is always returned.
|
||||
CommitWithParents(v types.Value, p SetOfCommit) (DataStore, bool)
|
||||
}
|
||||
|
||||
func NewDataStore(cs chunks.ChunkStore) DataStore {
|
||||
return newDataStoreInternal(cs)
|
||||
}
|
||||
|
||||
func newDataStoreInternal(cs chunks.ChunkStore) DataStore {
|
||||
if (cs.Root() == ref.Ref{}) {
|
||||
return DataStore{cs, nil}
|
||||
}
|
||||
return DataStore{cs, commitFromRef(cs.Root(), cs)}
|
||||
}
|
||||
|
||||
func commitFromRef(commitRef ref.Ref, cs chunks.ChunkSource) *Commit {
|
||||
c := CommitFromVal(types.ReadValue(commitRef, cs))
|
||||
return &c
|
||||
}
|
||||
|
||||
// MaybeHead returns the current Head Commit of this Datastore, which contains the current root of the DataStore's value tree, if available. If not, it returns a new Commit and 'false'.
|
||||
func (ds *DataStore) MaybeHead() (Commit, bool) {
|
||||
if ds.head == nil {
|
||||
return NewCommit(), false
|
||||
}
|
||||
return *ds.head, true
|
||||
}
|
||||
|
||||
// Head returns the current head Commit, which contains the current root of the DataStore's value tree.
|
||||
func (ds *DataStore) Head() Commit {
|
||||
c, ok := ds.MaybeHead()
|
||||
d.Chk.True(ok, "DataStore has no Head.")
|
||||
return c
|
||||
}
|
||||
|
||||
// Commit updates the commit that a datastore points at. The new Commit is constructed using v and the current Head.
|
||||
// If the update cannot be performed, e.g., because of a conflict, Commit returns 'false' and the current snapshot of the datastore so that the client can merge the changes and try again.
|
||||
func (ds *DataStore) Commit(v types.Value) (DataStore, bool) {
|
||||
p := NewSetOfCommit()
|
||||
if head, ok := ds.MaybeHead(); ok {
|
||||
p = p.Insert(head)
|
||||
}
|
||||
return ds.CommitWithParents(v, p)
|
||||
}
|
||||
|
||||
// CommitWithParents updates the commit that a datastore points at. The new Commit is constructed using v and p.
|
||||
// If the update cannot be performed, e.g., because of a conflict, CommitWithParents returns 'false' and the current snapshot of the datastore so that the client can merge the changes and try again.
|
||||
func (ds *DataStore) CommitWithParents(v types.Value, p SetOfCommit) (DataStore, bool) {
|
||||
ok := ds.doCommit(NewCommit().SetParents(p.NomsValue()).SetValue(v))
|
||||
return newDataStoreInternal(ds.ChunkStore), ok
|
||||
}
|
||||
|
||||
// doCommit manages concurrent access the single logical piece of mutable state: the current head. doCommit is optimistic in that it is attempting to update head making the assumption that currentRootRef is the ref of the current head. The call to UpdateRoot below will fail if that assumption fails (e.g. because of a race with another writer) and the entire algorithm must be tried again.
|
||||
func (ds *DataStore) doCommit(commit Commit) bool {
|
||||
currentRootRef := ds.Root()
|
||||
|
||||
// Note: |currentHead| may be different from ds.head and *must* be consistent with currentRootRef.
|
||||
// If ds.head is nil, then any commit is allowed.
|
||||
if ds.head != nil {
|
||||
var currentHead Commit
|
||||
if currentRootRef == ds.head.Ref() {
|
||||
currentHead = *ds.head
|
||||
} else {
|
||||
currentHead = *commitFromRef(currentRootRef, ds)
|
||||
}
|
||||
|
||||
// Allow only fast-forward commits.
|
||||
if commit.Equals(currentHead) {
|
||||
return true
|
||||
} else if !descendsFrom(commit, currentHead) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// TODO: This Commit will be orphaned if this UpdateRoot below fails
|
||||
newRootRef := types.WriteValue(commit.NomsValue(), ds)
|
||||
|
||||
ok := ds.UpdateRoot(newRootRef, currentRootRef)
|
||||
return ok
|
||||
}
|
||||
|
||||
func descendsFrom(commit, currentHead Commit) bool {
|
||||
// BFS because the common case is that the ancestor is only a step or two away
|
||||
ancestors := NewSetOfCommit().Insert(commit)
|
||||
for !ancestors.Has(currentHead) {
|
||||
if ancestors.Empty() {
|
||||
return false
|
||||
}
|
||||
ancestors = getAncestors(ancestors)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getAncestors(commits SetOfCommit) SetOfCommit {
|
||||
ancestors := NewSetOfCommit()
|
||||
commits.Iter(func(c Commit) (stop bool) {
|
||||
ancestors =
|
||||
ancestors.Union(SetOfCommitFromVal(c.Parents()))
|
||||
return
|
||||
})
|
||||
return ancestors
|
||||
return newLocalDataStore(cs)
|
||||
}
|
||||
|
||||
type Flags struct {
|
||||
cflags chunks.Flags
|
||||
ldb chunks.LevelDBStoreFlags
|
||||
memory chunks.MemoryStoreFlags
|
||||
nop chunks.NopStoreFlags
|
||||
hflags http.Flags
|
||||
}
|
||||
|
||||
@@ -123,20 +39,24 @@ func NewFlags() Flags {
|
||||
|
||||
func NewFlagsWithPrefix(prefix string) Flags {
|
||||
return Flags{
|
||||
chunks.NewFlagsWithPrefix(prefix),
|
||||
chunks.LevelDBFlags(prefix),
|
||||
chunks.MemoryFlags(prefix),
|
||||
chunks.NopFlags(prefix),
|
||||
http.NewFlagsWithPrefix(prefix),
|
||||
}
|
||||
}
|
||||
|
||||
func (f Flags) CreateDataStore() (DataStore, bool) {
|
||||
cs := f.cflags.CreateStore()
|
||||
if cs == nil {
|
||||
cs = f.hflags.CreateClient()
|
||||
if cs == nil {
|
||||
return DataStore{}, false
|
||||
}
|
||||
var cs chunks.ChunkStore
|
||||
if cs = f.ldb.CreateStore(); cs != nil {
|
||||
} else if cs = f.memory.CreateStore(); cs != nil {
|
||||
} else if cs = f.nop.CreateStore(); cs != nil {
|
||||
} else if cs = f.hflags.CreateClient(); cs != nil {
|
||||
}
|
||||
|
||||
ds := NewDataStore(cs)
|
||||
return ds, true
|
||||
if cs == nil {
|
||||
return &LocalDataStore{}, false
|
||||
}
|
||||
|
||||
return newLocalDataStore(cs), true
|
||||
}
|
||||
|
||||
94
datas/datastore_common.go
Normal file
94
datas/datastore_common.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package datas
|
||||
|
||||
import (
|
||||
"github.com/attic-labs/noms/chunks"
|
||||
"github.com/attic-labs/noms/d"
|
||||
"github.com/attic-labs/noms/ref"
|
||||
"github.com/attic-labs/noms/types"
|
||||
)
|
||||
|
||||
// DataStore provides versioned storage for noms values. Each DataStore instance represents one moment in history. Heads() returns the Commit from each active fork at that moment. The Commit() method returns a new DataStore, representing a new moment in history.
|
||||
type DataStoreCommon struct {
|
||||
chunks.ChunkStore
|
||||
head *Commit
|
||||
}
|
||||
|
||||
func commitFromRef(commitRef ref.Ref, cs chunks.ChunkSource) *Commit {
|
||||
c := CommitFromVal(types.ReadValue(commitRef, cs))
|
||||
return &c
|
||||
}
|
||||
|
||||
func (ds *DataStoreCommon) MaybeHead() (Commit, bool) {
|
||||
if ds.head == nil {
|
||||
return NewCommit(), false
|
||||
}
|
||||
return *ds.head, true
|
||||
}
|
||||
|
||||
func (ds *DataStoreCommon) Head() Commit {
|
||||
c, ok := ds.MaybeHead()
|
||||
d.Chk.True(ok, "DataStore has no Head.")
|
||||
return c
|
||||
}
|
||||
|
||||
func (ds *DataStoreCommon) commit(v types.Value) bool {
|
||||
p := NewSetOfCommit()
|
||||
if head, ok := ds.MaybeHead(); ok {
|
||||
p = p.Insert(head)
|
||||
}
|
||||
return ds.commitWithParents(v, p)
|
||||
}
|
||||
|
||||
func (ds *DataStoreCommon) commitWithParents(v types.Value, p SetOfCommit) bool {
|
||||
return ds.doCommit(NewCommit().SetParents(p.NomsValue()).SetValue(v))
|
||||
}
|
||||
|
||||
// doCommit manages concurrent access the single logical piece of mutable state: the current head. doCommit is optimistic in that it is attempting to update head making the assumption that currentRootRef is the ref of the current head. The call to UpdateRoot below will fail if that assumption fails (e.g. because of a race with another writer) and the entire algorithm must be tried again.
|
||||
func (ds *DataStoreCommon) doCommit(commit Commit) bool {
|
||||
currentRootRef := ds.Root()
|
||||
|
||||
// Note: |currentHead| may be different from ds.head and *must* be consistent with currentRootRef.
|
||||
// If ds.head is nil, then any commit is allowed.
|
||||
if ds.head != nil {
|
||||
var currentHead Commit
|
||||
if currentRootRef == ds.head.Ref() {
|
||||
currentHead = *ds.head
|
||||
} else {
|
||||
currentHead = *commitFromRef(currentRootRef, ds)
|
||||
}
|
||||
|
||||
// Allow only fast-forward commits.
|
||||
if commit.Equals(currentHead) {
|
||||
return true
|
||||
} else if !descendsFrom(commit, currentHead) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// TODO: This Commit will be orphaned if this UpdateRoot below fails
|
||||
newRootRef := types.WriteValue(commit.NomsValue(), ds)
|
||||
|
||||
ok := ds.UpdateRoot(newRootRef, currentRootRef)
|
||||
return ok
|
||||
}
|
||||
|
||||
func descendsFrom(commit, currentHead Commit) bool {
|
||||
// BFS because the common case is that the ancestor is only a step or two away
|
||||
ancestors := NewSetOfCommit().Insert(commit)
|
||||
for !ancestors.Has(currentHead) {
|
||||
if ancestors.Empty() {
|
||||
return false
|
||||
}
|
||||
ancestors = getAncestors(ancestors)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getAncestors(commits SetOfCommit) SetOfCommit {
|
||||
ancestors := NewSetOfCommit()
|
||||
commits.Iter(func(c Commit) (stop bool) {
|
||||
ancestors =
|
||||
ancestors.Union(SetOfCommitFromVal(c.Parents()))
|
||||
return
|
||||
})
|
||||
return ancestors
|
||||
}
|
||||
38
datas/local_datastore.go
Normal file
38
datas/local_datastore.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package datas
|
||||
|
||||
import (
|
||||
"github.com/attic-labs/noms/chunks"
|
||||
"github.com/attic-labs/noms/ref"
|
||||
"github.com/attic-labs/noms/types"
|
||||
)
|
||||
|
||||
// DataStore provides versioned storage for noms values. Each DataStore instance represents one moment in history. Heads() returns the Commit from each active fork at that moment. The Commit() method returns a new DataStore, representing a new moment in history.
|
||||
type LocalDataStore struct {
|
||||
DataStoreCommon
|
||||
}
|
||||
|
||||
func newLocalDataStore(cs chunks.ChunkStore) *LocalDataStore {
|
||||
rootRef := cs.Root()
|
||||
if rootRef == (ref.Ref{}) {
|
||||
return &LocalDataStore{DataStoreCommon{cs, nil}}
|
||||
}
|
||||
|
||||
return &LocalDataStore{DataStoreCommon{cs, commitFromRef(rootRef, cs)}}
|
||||
}
|
||||
|
||||
func newDataStoreInternal(cs chunks.ChunkStore) DataStoreCommon {
|
||||
if (cs.Root() == ref.Ref{}) {
|
||||
return DataStoreCommon{cs, nil}
|
||||
}
|
||||
return DataStoreCommon{cs, commitFromRef(cs.Root(), cs)}
|
||||
}
|
||||
|
||||
func (lds *LocalDataStore) Commit(v types.Value) (DataStore, bool) {
|
||||
ok := lds.commit(v)
|
||||
return newLocalDataStore(lds.ChunkStore), ok
|
||||
}
|
||||
|
||||
func (lds *LocalDataStore) CommitWithParents(v types.Value, p SetOfCommit) (DataStore, bool) {
|
||||
ok := lds.commitWithParents(v, p)
|
||||
return newLocalDataStore(lds.ChunkStore), ok
|
||||
}
|
||||
Reference in New Issue
Block a user