github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/datas/database.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  // Package datas defines and implements the database layer used in Noms.
    23  package datas
    24  
    25  import (
    26  	"context"
    27  	"io"
    28  
    29  	"github.com/dolthub/dolt/go/store/hash"
    30  	"github.com/dolthub/dolt/go/store/nbs"
    31  
    32  	"github.com/dolthub/dolt/go/store/chunks"
    33  	"github.com/dolthub/dolt/go/store/types"
    34  )
    35  
    36  // Database provides versioned storage for noms values. While Values can be
    37  // directly read and written from a Database, it is generally more appropriate
    38  // to read data by inspecting the Head of a Dataset and write new data by
    39  // updating the Head of a Dataset via Commit() or similar. Particularly, new
    40  // data is not guaranteed to be persistent until after a Commit (Delete,
    41  // SetHeadToCommit, or FastForward) operation completes.
    42  // The Database API is stateful, meaning that calls to GetDataset() or
    43  // Datasets() occurring after a call to Commit() (et al) will represent the
    44  // result of the Commit().
    45  type Database interface {
    46  	// To implement types.ValueWriter, Database implementations provide
    47  	// WriteValue(). WriteValue() writes v to this Database, though v is not
    48  	// guaranteed to be be persistent until after a subsequent Commit(). The
    49  	// return value is the Ref of v.
    50  	// Written values won't be persisted until a commit-alike
    51  	types.ValueReadWriter
    52  
    53  	// Close must have no side-effects
    54  	io.Closer
    55  
    56  	// Datasets returns the root of the database which is a
    57  	// Map<String, Ref<Commit>> where string is a datasetID.
    58  	Datasets(ctx context.Context) (types.Map, error)
    59  
    60  	// GetDataset returns a Dataset struct containing the current mapping of
    61  	// datasetID in the above Datasets Map.
    62  	GetDataset(ctx context.Context, datasetID string) (Dataset, error)
    63  
    64  	// Rebase brings this Database's view of the world inline with upstream.
    65  	Rebase(ctx context.Context) error
    66  
    67  	// Commit updates the Commit that ds.ID() in this database points at. All
    68  	// Values that have been written to this Database are guaranteed to be
    69  	// persistent after Commit() returns.
    70  	// The new Commit struct is constructed using v, opts.Parents, and
    71  	// opts.Meta. If opts.Parents is the zero value (types.Set{}) then
    72  	// the current head is used. If opts.Meta is the zero value
    73  	// (types.Struct{}) then a fully initialized empty Struct is passed to
    74  	// NewCommit.
    75  	// The returned Dataset is always the newest snapshot, regardless of
    76  	// success or failure, and Datasets() is updated to match backing storage
    77  	// upon return as well. If the update cannot be performed, e.g., because
    78  	// of a conflict, Commit returns an 'ErrMergeNeeded' error.
    79  	Commit(ctx context.Context, ds Dataset, v types.Value, opts CommitOptions) (Dataset, error)
    80  
    81  	// CommitDangling creates a new commit that is unreferenced by any Dataset.
    82  	// This method is used in the course of programmatic updates such as Rebase
    83  	// All Values that have been written to this Database are guaranteed to be
    84  	// persistent after CommitDangling() returns.
    85  	// The new Commit struct is of the same form as structs created by Commit()
    86  	CommitDangling(ctx context.Context, v types.Value, opts CommitOptions) (types.Struct, error)
    87  
    88  	// CommitValue updates the Commit that ds.ID() in this database points at.
    89  	// All Values that have been written to this Database are guaranteed to be
    90  	// persistent after Commit().
    91  	// The new Commit struct is constructed using `v`, and the current Head of
    92  	// `ds` as the lone Parent.
    93  	// The returned Dataset is always the newest snapshot, regardless of
    94  	// success or failure, and Datasets() is updated to match backing storage
    95  	// upon return as well. If the update cannot be performed, e.g., because
    96  	// of a conflict, Commit returns an 'ErrMergeNeeded' error.
    97  	CommitValue(ctx context.Context, ds Dataset, v types.Value) (Dataset, error)
    98  
    99  	// Tag stores an immutable reference to a Value. It takes a Ref and a Dataset
   100  	// whose head must be nil (ie a newly created Dataset).
   101  	// The new Tag struct is constructed with `ref` and metadata about the tag
   102  	// contained in the struct `opts.Meta`.
   103  	// The returned Dataset is always the newest snapshot, regardless of
   104  	// success or failure, and Datasets() is updated to match backing storage
   105  	// upon return as well.
   106  	Tag(ctx context.Context, ds Dataset, ref types.Ref, opts TagOptions) (Dataset, error)
   107  
   108  	// UpdateWorkingSet updates the dataset given, setting its value to a new
   109  	// working set value object with the ref and meta given. If the dataset given
   110  	// already had a value, it must match the hash given or this method returns
   111  	// ErrOptimisticLockFailed and the caller must retry.
   112  	// The returned Dataset is always the newest snapshot, regardless of
   113  	// success or failure, and Datasets() is updated to match backing storage
   114  	// upon return as well.
   115  	UpdateWorkingSet(ctx context.Context, ds Dataset, workingSetValue types.Ref, meta WorkingSetMeta, prevHash hash.Hash) (Dataset, error)
   116  
   117  	// Delete removes the Dataset named ds.ID() from the map at the root of
   118  	// the Database. The Dataset data is not necessarily cleaned up at this
   119  	// time, but may be garbage collected in the future.
   120  	// The returned Dataset is always the newest snapshot, regardless of
   121  	// success or failure, and Datasets() is updated to match backing storage
   122  	// upon return as well. If the update cannot be performed, e.g., because
   123  	// of a conflict, Delete returns an 'ErrMergeNeeded' error.
   124  	Delete(ctx context.Context, ds Dataset) (Dataset, error)
   125  
   126  	// SetHead ignores any lineage constraints (e.g. the current Head being in
   127  	// commit’s Parent set) and force-sets a mapping from datasetID: commit in
   128  	// this database.
   129  	// All Values that have been written to this Database are guaranteed to be
   130  	// persistent after SetHead(). If the update cannot be performed, e.g.,
   131  	// because another process moved the current Head out from under you,
   132  	// error will be non-nil.
   133  	// The newest snapshot of the Dataset is always returned, so the caller an
   134  	// easily retry using the latest.
   135  	// Regardless, Datasets() is updated to match backing storage upon return.
   136  	SetHead(ctx context.Context, ds Dataset, newHeadRef types.Ref) (Dataset, error)
   137  
   138  	// FastForward takes a types.Ref to a Commit object and makes it the new
   139  	// Head of ds iff it is a descendant of the current Head. Intended to be
   140  	// used e.g. after a call to Pull(). If the update cannot be performed,
   141  	// e.g., because another process moved the current Head out from under
   142  	// you, err will be non-nil.
   143  	// The newest snapshot of the Dataset is always returned, so the caller
   144  	// can easily retry using the latest.
   145  	// Regardless, Datasets() is updated to match backing storage upon return.
   146  	FastForward(ctx context.Context, ds Dataset, newHeadRef types.Ref) (Dataset, error)
   147  
   148  	// Stats may return some kind of struct that reports statistics about the
   149  	// ChunkStore that backs this Database instance. The type is
   150  	// implementation-dependent, and impls may return nil
   151  	Stats() interface{}
   152  
   153  	// StatsSummary may return a string containing summarized statistics for
   154  	// the ChunkStore that backs this Database. It must return "Unsupported"
   155  	// if this operation is not supported.
   156  	StatsSummary() string
   157  
   158  	Flush(ctx context.Context) error
   159  
   160  	// chunkStore returns the ChunkStore used to read and write
   161  	// groups of values to the database efficiently. This interface is a low-
   162  	// level detail of the database that should infrequently be needed by
   163  	// clients.
   164  	chunkStore() chunks.ChunkStore
   165  }
   166  
   167  func NewDatabase(cs chunks.ChunkStore) Database {
   168  	return newDatabase(cs)
   169  }
   170  
   171  // GarbageCollector provides a method to
   172  // remove unreferenced data from a store.
   173  type GarbageCollector interface {
   174  	types.ValueReadWriter
   175  
   176  	// GC traverses the database starting at the Root and removes
   177  	// all unreferenced data from persistent storage.
   178  	GC(ctx context.Context) error
   179  }
   180  
   181  // CanUsePuller returns true if a datas.Puller can be used to pull data from one Database into another.  Not all
   182  // Databases support this yet.
   183  func CanUsePuller(db Database) bool {
   184  	cs := db.chunkStore()
   185  	if tfs, ok := cs.(nbs.TableFileStore); ok {
   186  		ops := tfs.SupportedOperations()
   187  		return ops.CanRead && ops.CanWrite
   188  	}
   189  	return false
   190  }
   191  
   192  func GetCSStatSummaryForDB(db Database) string {
   193  	cs := db.chunkStore()
   194  	return cs.StatsSummary()
   195  }