github.com/ipld/go-ipld-prime@v0.21.0/storage/api.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  )
     7  
     8  // --- basics --->
     9  
    10  // Storage is one of the base interfaces in the storage APIs.
    11  // This type is rarely seen by itself alone (and never useful to implement alone),
    12  // but is included in both ReadableStorage and WritableStorage.
    13  // Because it's included in both the of the other two useful base interfaces,
    14  // you can define functions that work on either one of them
    15  // by using this type to describe your function's parameters.
    16  //
    17  // Library functions that work with storage systems should take either
    18  // ReadableStorage, or WritableStorage, or Storage, as a parameter,
    19  // depending on whether the function deals with the reading of data,
    20  // or the writing of data, or may be found on either, respectively.
    21  //
    22  // An implementation of Storage may also support many other methods.
    23  // At the very least, it should also support one of either ReadableStorage or WritableStorage.
    24  // It may support even more interfaces beyond that for additional feature detection.
    25  // See the package-wide docs for more discussion of this design.
    26  //
    27  // The Storage interface does not include much of use in itself alone,
    28  // because ReadableStorage and WritableStorage are meant to be the most used types in declarations.
    29  // However, it does include the Has function, because that function is reasonable to require ubiquitously from all implementations,
    30  // and it serves as a reasonable marker to make sure the Storage interface is not trivially satisfied.
    31  type Storage interface {
    32  	Has(ctx context.Context, key string) (bool, error)
    33  }
    34  
    35  // ReadableStorage is one of the base interfaces in the storage APIs;
    36  // a storage system should implement at minimum either this, or WritableStorage,
    37  // depending on whether it supports reading or writing.
    38  // (One type may also implement both.)
    39  //
    40  // ReadableStorage implementations must at minimum provide
    41  // a way to ask the store whether it contains a key,
    42  // and a way to ask it to return the value.
    43  //
    44  // Library functions that work with storage systems should take either
    45  // ReadableStorage, or WritableStorage, or Storage, as a parameter,
    46  // depending on whether the function deals with the reading of data,
    47  // or the writing of data, or may be found on either, respectively.
    48  //
    49  // An implementation of ReadableStorage may also support many other methods --
    50  // for example, it may additionally match StreamingReadableStorage, or yet more interfaces.
    51  // Usually, you should not need to check for this yourself; instead,
    52  // you should use the storage package's functions to ask for the desired mode of interaction.
    53  // Those functions will will accept any ReadableStorage as an argument,
    54  // detect the additional interfaces automatically and use them if present,
    55  // or, fall back to synthesizing equivalent behaviors from the basics.
    56  // See the package-wide docs for more discussion of this design.
    57  type ReadableStorage interface {
    58  	Storage
    59  	Get(ctx context.Context, key string) ([]byte, error)
    60  }
    61  
    62  // WritableStorage is one of the base interfaces in the storage APIs;
    63  // a storage system should implement at minimum either this, or ReadableStorage,
    64  // depending on whether it supports reading or writing.
    65  // (One type may also implement both.)
    66  //
    67  // WritableStorage implementations must at minimum provide
    68  // a way to ask the store whether it contains a key,
    69  // and a way to put a value into storage indexed by some key.
    70  //
    71  // Library functions that work with storage systems should take either
    72  // ReadableStorage, or WritableStorage, or Storage, as a parameter,
    73  // depending on whether the function deals with the reading of data,
    74  // or the writing of data, or may be found on either, respectively.
    75  //
    76  // An implementation of WritableStorage may also support many other methods --
    77  // for example, it may additionally match StreamingWritableStorage, or yet more interfaces.
    78  // Usually, you should not need to check for this yourself; instead,
    79  // you should use the storage package's functions to ask for the desired mode of interaction.
    80  // Those functions will will accept any WritableStorage as an argument,
    81  // detect the additional interfaces automatically and use them if present,
    82  // or, fall back to synthesizing equivalent behaviors from the basics.
    83  // See the package-wide docs for more discussion of this design.
    84  type WritableStorage interface {
    85  	Storage
    86  	Put(ctx context.Context, key string, content []byte) error
    87  }
    88  
    89  // --- streaming --->
    90  
    91  type StreamingReadableStorage interface {
    92  	GetStream(ctx context.Context, key string) (io.ReadCloser, error)
    93  }
    94  
    95  // StreamingWritableStorage is a feature-detection interface that advertises support for streaming writes.
    96  // It is normal for APIs to use WritableStorage in their exported API surface,
    97  // and then internally check if that value implements StreamingWritableStorage if they wish to use streaming operations.
    98  //
    99  // Streaming writes can be preferable to the all-in-one style of writing of WritableStorage.Put,
   100  // because with streaming writes, the high water mark for memory usage can be kept lower.
   101  // On the other hand, streaming writes can incur slightly higher allocation counts,
   102  // which may cause some performance overhead when handling many small writes in sequence.
   103  //
   104  // The PutStream function returns three parameters: an io.Writer (as you'd expect), another function, and an error.
   105  // The function returned is called a "WriteCommitter".
   106  // The final error value is as usual: it will contain an error value if the write could not be begun.
   107  // ("WriteCommitter" will be refered to as such throughout the docs, but we don't give it a named type --
   108  // unfortunately, this is important, because we don't want to force implementers of storage systems to import this package just for a type name.)
   109  //
   110  // The WriteCommitter function should be called when you're done writing,
   111  // at which time you give it the key you want to commit the data as.
   112  // It will close and flush any streams, and commit the data to its final location under this key.
   113  // (If the io.Writer is also an io.WriteCloser, it is not necessary to call Close on it,
   114  // because using the WriteCommiter will do this for you.)
   115  //
   116  // Because these storage APIs are meant to work well for content-addressed systems,
   117  // the key argument is not provided at the start of the write -- it's provided at the end.
   118  // (This gives the opportunity to be computing a hash of the contents as they're written to the stream.)
   119  //
   120  // As a special case, giving a key of the zero string to the WriteCommiter will
   121  // instead close and remove any temp files, and store nothing.
   122  // An error may still be returned from the WriteCommitter if there is an error cleaning up
   123  // any temporary storage buffers that were created.
   124  //
   125  // Continuing to write to the io.Writer after calling the WriteCommitter function will result in errors.
   126  // Calling the WriteCommitter function more than once will result in errors.
   127  type StreamingWritableStorage interface {
   128  	PutStream(ctx context.Context) (io.Writer, func(key string) error, error)
   129  }
   130  
   131  // --- other specializations --->
   132  
   133  // VectorWritableStorage is an API for writing several slices of bytes at once into storage.
   134  // It's meant a feature-detection interface; not all storage implementations need to provide this feature.
   135  // This kind of API can be useful for maximizing performance in scenarios where
   136  // data is already loaded completely into memory, but scattered across several non-contiguous regions.
   137  type VectorWritableStorage interface {
   138  	PutVec(ctx context.Context, key string, blobVec [][]byte) error
   139  }
   140  
   141  // PeekableStorage is a feature-detection interface which a storage implementation can use to advertise
   142  // the ability to look at a piece of data, and return it in shared memory.
   143  // The PeekableStorage.Peek method is essentially the same as ReadableStorage.Get --
   144  // but by contrast, ReadableStorage is expected to return a safe copy.
   145  // PeekableStorage can be used when the caller knows they will not mutate the returned slice.
   146  //
   147  // An io.Closer is returned along with the byte slice.
   148  // The Close method on the Closer must be called when the caller is done with the byte slice;
   149  // otherwise, memory leaks may result.
   150  // (Implementers of this interface may be expecting to reuse the byte slice after Close is called.)
   151  //
   152  // Note that Peek does not imply that the caller can use the byte slice freely;
   153  // doing so may result in storage corruption or other undefined behavior.
   154  type PeekableStorage interface {
   155  	Peek(ctx context.Context, key string) ([]byte, io.Closer, error)
   156  }
   157  
   158  // the following are all hypothetical additional future interfaces (in varying degress of speculativeness):
   159  
   160  // FUTURE: an EnumerableStorage API, that lets you list all keys present?
   161  
   162  // FUTURE: a cleanup API (for getting rid of tmp files that might've been left behind on rough shutdown)?
   163  
   164  // FUTURE: a sync-forcing API?
   165  
   166  // FUTURE: a delete API?  sure.  (just document carefully what its consistency model is -- i.e. basically none.)
   167  //   (hunch: if you do want some sort of consistency model -- consider offering a whole family of methods that have some sort of generation or sequencing number on them.)
   168  
   169  // FUTURE: a force-overwrite API?  (not useful for a content-address system.  but maybe a gesture towards wider reusability is acceptable to have on offer.)
   170  
   171  // FUTURE: a size estimation API?  (unclear if we need to standardize this, but we could.  an offer, anyway.)
   172  
   173  // FUTURE: a GC API?  (dubious -- doing it well probably crosses logical domains, and should not be tied down here.)