go.temporal.io/server@v1.23.0/common/archiver/README.md (about)

     1  ## What
     2  
     3  This README explains how to add new Archiver implementations.
     4  
     5  ## Steps
     6  
     7  **Step 1: Create a new package for your implementation**
     8  
     9  Create a new directory in the `archiver` folder. The structure should look like the following:
    10  ```
    11  ./common/archiver
    12    - filestore/                      -- Filestore implementation 
    13    - provider/
    14        - provider.go                 -- Provider of archiver instances
    15    - yourImplementation/
    16        - historyArchiver.go          -- HistoryArchiver implementation
    17        - historyArchiver_test.go     -- Unit tests for HistoryArchiver
    18        - visibilityArchiver.go       -- VisibilityArchiver implementations
    19        - visibilityArchiver_test.go  -- Unit tests for VisibilityArchiver
    20  ```
    21  
    22  **Step 2: Implement the HistoryArchiver interface**
    23  
    24  ```go
    25  type HistoryArchiver interface {
    26      // Archive is used to archive a workflow history. When the context expires the method should stop trying to archive.
    27      // Implementors are free to archive however they want, including implementing retries of sub-operations. The URI defines
    28      // the resource that histories should be archived into. The implementor gets to determine how to interpret the URI.
    29      // The Archive method may or may not be automatically retried by the caller. The ArchiveOptions are used
    30      // to interact with these retries including giving the implementor the ability to cancel retries and record progress
    31      // between retry attempts. 
    32      // This method will be invoked after a workflow passes its retention period.
    33      // It's possible that this method will be invoked for one workflow multiple times and potentially concurrently,
    34      // implementation should correctly handle the workflow not exist case and return nil error.
    35      Archive(context.Context, URI, *ArchiveHistoryRequest, ...ArchiveOption) error
    36      
    37      // Get is used to access an archived history. When context expires method should stop trying to fetch history.
    38      // The URI identifies the resource from which history should be accessed and it is up to the implementor to interpret this URI.
    39      // This method should thrift errors - see filestore as an example.
    40      Get(context.Context, URI, *GetHistoryRequest) (*GetHistoryResponse, error)
    41      
    42      // ValidateURI is used to define what a valid URI for an implementation is.
    43      ValidateURI(URI) error
    44  }
    45  ```
    46  
    47  **Step 3: Implement the VisibilityArchiver interface**
    48  
    49  ```go
    50  type VisibilityArchiver interface {
    51      // Archive is used to archive one workflow visibility record. 
    52      // Check the Archive() method of the HistoryArchiver interface in Step 2 for parameters' meaning and requirements. 
    53      // The only difference is that the ArchiveOption parameter won't include an option for recording process. 
    54      // Please make sure your implementation is lossless. If any in-memory batching mechanism is used, then those batched records will be lost during server restarts. 
    55      // This method will be invoked when workflow closes. Note that because of conflict resolution, it is possible for a workflow to through the closing process multiple times, which means that this method can be invoked more than once after a workflow closes.
    56      Archive(context.Context, URI, *ArchiveVisibilityRequest, ...ArchiveOption) error
    57      
    58      // Query is used to retrieve archived visibility records. 
    59      // Check the Get() method of the HistoryArchiver interface in Step 2 for parameters' meaning and requirements.
    60      // The request includes a string field called query, which describes what kind of visibility records should be returned. For example, it can be some SQL-like syntax query string. 
    61      // Your implementation is responsible for parsing and validating the query, and also returning all visibility records that match the query. 
    62      // Currently the maximum context timeout passed into the method is 3 minutes, so it's ok if this method takes a long time to run.
    63      Query(context.Context, URI, *QueryVisibilityRequest) (*QueryVisibilityResponse, error)
    64  
    65      // ValidateURI is used to define what a valid URI for an implementation is.
    66      ValidateURI(URI) error
    67  }
    68  ```
    69  
    70  **Step 4: Update provider to provide access to your implementation**
    71  
    72  Modify the `./provider/provider.go` file so that the `ArchiverProvider` knows how to create an instance of your archiver. 
    73  Also, add configs for you archiver to static yaml config files and modify the `HistoryArchiverProvider` 
    74  and `VisibilityArchiverProvider` struct in the `../common/service/config.go` accordingly.
    75  
    76  
    77  ## FAQ
    78  **If my Archive method can automatically be retried by caller how can I record and access progress between retries?**
    79  
    80  ArchiverOptions is used to handle this. The following shows and example: 
    81  ```go
    82  func (a *Archiver) Archive(
    83  	ctx context.Context,
    84  	URI string,
    85  	request *ArchiveRequest,
    86  	opts ...ArchiveOption,
    87  ) error {
    88    featureCatalog := GetFeatureCatalog(opts...) // this function is defined in options.go
    89  
    90    var progress progress
    91  
    92    // Check if the feature for recording progress is enabled.
    93    if featureCatalog.ProgressManager != nil {
    94      if err := featureCatalog.ProgressManager.LoadProgress(ctx, &prevProgress); err != nil {
    95        // log some error message and return error if needed.
    96      }
    97    }
    98  
    99    // Your archiver implementation...
   100  
   101    // Record current progress
   102    if featureCatalog.ProgressManager != nil {
   103      if err := featureCatalog.ProgressManager.RecordProgress(ctx, progress); err != nil {
   104        // log some error message and return error if needed. 
   105      }
   106    }
   107  }
   108  ```
   109  
   110  **If my Archive method encounters an error which is non-retryable how do I indicate that the caller should not retry?**
   111  
   112  ```go
   113  func (a *Archiver) Archive(
   114  	ctx context.Context,
   115  	URI string,
   116  	request *ArchiveRequest,
   117  	opts ...ArchiveOption,
   118  ) error {
   119    featureCatalog := GetFeatureCatalog(opts...) // this function is defined in options.go
   120  
   121    err := youArchiverImpl()
   122    if nonRetryableErr(err) {
   123      if featureCatalog.NonRetryableError != nil {
   124  	  return featureCatalog.NonRetryableError() // when the caller gets this error type back it will not retry anymore.
   125      }
   126    }
   127  }
   128  ```
   129  
   130  **How does my history archiver implementation read history?**
   131  
   132  The `archiver` package provides a utility class called `HistoryIterator` which is a wrapper of `ExecutionManager`. 
   133  Its usage is simpler than the `ExecutionManager` given in the `BootstrapContainer`, 
   134  so archiver implementations can choose to use it when reading workflow histories. 
   135  See the `historyIterator.go` file for more details. 
   136  Sample usage can be found in the filestore historyArchiver implementation.
   137  
   138  **Should my archiver define all its own error types?**
   139  
   140  Each archiver is free to define and return any errors it wants. However many common errors which
   141  exist between archivers are already defined in `constants.go`.
   142  
   143  **Is there a generic query syntax for visibility archiver?**
   144  
   145  Currently no. But this is something we plan to do in the future. As for now, try to make your syntax similar to the one used by our advanced list workflow API.