github.com/yandex/pandora@v0.5.32/core/core.go (about)

     1  // package core defines pandora engine extension points.
     2  // Core interfaces implementations MAY be used for custom engine creation and using as a library,
     3  // or MAY be registered in pandora plugin system (look at core/plugin package), for creating engine
     4  // from abstract config.
     5  //
     6  // The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT",
     7  // "RECOMMENDED",  "MAY", and "OPTIONAL" in that package doc are to be interpreted as described in
     8  // https://www.ietf.org/rfc/rfc2119.txt
     9  package core
    10  
    11  import (
    12  	"context"
    13  	"io"
    14  	"time"
    15  
    16  	"go.uber.org/zap"
    17  )
    18  
    19  // Ammo is data required for one shot. SHOULD contains something that differs
    20  // from one shot to another. Something like requested recourse indetificator, query params,
    21  // meta information helpful for future shooting analysis.
    22  // Information common for all shoots SHOULD be passed via Provider configuration.
    23  type Ammo interface{}
    24  
    25  // ResettableAmmo is ammo that can be efficiently reset before reuse.
    26  // Generic Provider (Provider that accepts undefined type of Ammo) SHOULD Reset Ammo before reuse
    27  // if it implements ResettableAmmo.
    28  // Ammo that is not going to be used with generic Providers don't need to implement ResettableAmmo.
    29  type ResettableAmmo interface {
    30  	Ammo
    31  	Reset()
    32  }
    33  
    34  //go:generate mockery --name=Provider --case=underscore --outpkg=coremock
    35  
    36  // Provider is routine that generates ammo for Instance shoots.
    37  // A Provider MUST be goroutine safe.
    38  type Provider interface {
    39  	// Run starts provider routine of ammo  generation.
    40  	// Blocks until ammo finish, error or context cancel.
    41  	// Run MUST be called only once. Run SHOULD be called before Acquire or Release calls, but
    42  	// MAY NOT because of goroutine races.
    43  	// In case of ctx cancel, SHOULD return nil, but MAY ctx.Err(), or error caused ctx.Err()
    44  	// in terms of github.com/pkg/errors.Cause.
    45  	Run(ctx context.Context, deps ProviderDeps) error
    46  	// Acquire acquires ammo for shoot. Acquire SHOULD be lightweight, so Instance can Shoot as
    47  	// soon as possible. That means ammo format parsing SHOULD be done in Provider Run goroutine,
    48  	// but acquire just takes ammo from ready queue.
    49  	// Ok false means that shooting MUST be stopped because ammo finished or shooting is canceled.
    50  	// Acquire MAY be called before Run, but SHOULD block until Run is called.
    51  	Acquire() (ammo Ammo, ok bool)
    52  	// Release notifies that ammo usage is finished, and it can be reused.
    53  	// Instance MUST NOT retain references to released ammo.
    54  	Release(ammo Ammo)
    55  }
    56  
    57  // ProviderDeps are passed to Provider in Run.
    58  // WARN: another fields could be added in next MINOR versions.
    59  // That is NOT considered as a breaking compatibility change.
    60  type ProviderDeps struct {
    61  	Log    *zap.Logger
    62  	PoolID string
    63  }
    64  
    65  //go:generate mockery --name=Gun --case=underscore --outpkg=coremock
    66  
    67  // Gun represents logic of making shoots sequentially.
    68  // A Gun is owned by only Instance that uses it for shooting in cycle: Acquire Ammo from Provider ->
    69  // wait for next shoot schedule event -> Shoot with Gun.
    70  // Guns that also implements io.Closer will be Closed after Instance finish.
    71  // Rule of thumb: Guns that create resources which SHOULD be closed after Instance finish,
    72  // SHOULD implement io.Closer.
    73  // Example: Gun that makes HTTP requests through keep alive connection SHOULD close it in Close.
    74  type Gun interface {
    75  	// Bind passes dependencies required for shooting. MUST be called once before any Shoot call.
    76  	Bind(aggr Aggregator, deps GunDeps) error
    77  	// Shoot makes one shoot. Shoot means some abstract load operation: web service or database
    78  	// request, for example.
    79  	// During shoot Gun SHOULD Acquire one or more Samples and Report them to bound Aggregator.
    80  	// Shoot error that MAY mean service under load fail SHOULD be reported to Aggregator in sample
    81  	// and SHOULD be logged to deps.Log at zap.WarnLevel.
    82  	// For example, HTTP request fail SHOULD be Reported and logged,.
    83  	// In case of error, that SHOULD cancel shooting for all Instances Shoot MUST panic using error
    84  	// value describing the problem. That could be configuration error, unsupported Ammo type,
    85  	// situation when service under load doesn't support required protocol,
    86  	Shoot(ammo Ammo)
    87  
    88  	// io.Closer // OPTIONAL to implement. See Gun doc for details.
    89  }
    90  
    91  // GunDeps are passed to Gun before Instance Run.
    92  // WARN: another fields could be added in next MINOR versions.
    93  // That is NOT considered as a breaking compatibility change.
    94  type GunDeps struct {
    95  	// Ctx is canceled on shoot cancel or finish.
    96  	Ctx context.Context
    97  	// Log fields already contains Id's of Pool and Instance.
    98  	Log *zap.Logger
    99  	// Unique of Gun owning Instance. MAY be used for tagging Samples.
   100  	// Pool set's ids to Instances from 0, incrementing it after Instance Run.
   101  	// There is a race between Instances for Ammo Acquire, so it's not guaranteed, that
   102  	// Instance with lower InstanceId gets it's Ammo earlier.
   103  	InstanceID int
   104  	PoolID     string
   105  
   106  	Shared any
   107  
   108  	// TODO(skipor): https://github.com/yandex/pandora/issues/71
   109  	// Pass parallelism value. InstanceId MUST be -1 if parallelism > 1.
   110  }
   111  
   112  // Sample is data containing shoot report. Return code, timings, shoot meta information.
   113  type Sample interface{}
   114  
   115  //go:generate mockery --name=BorrowedSample --case=underscore --outpkg=coremock
   116  
   117  // BorrowedSample is Sample that was borrowed from pool, and SHOULD be returned by Aggregator,
   118  // after it will handle Sample.
   119  type BorrowedSample interface {
   120  	Sample
   121  	Return()
   122  }
   123  
   124  //go:generate mockery --name=Aggregator --case=underscore --outpkg=coremock
   125  
   126  // Aggregator is routine that aggregates Samples from all Pool Instances.
   127  // Usually aggregator is shooting result reporter, that writes Reported Samples
   128  // to DataSink in machine readable format for future analysis.
   129  // An Aggregator MUST be goroutine safe.
   130  // GunDeps are passed to Gun before Instance Run.
   131  type Aggregator interface {
   132  	// Run starts aggregator routine of handling Samples. Blocks until fail or context cancel.
   133  	// Run MUST be called only once. Run SHOULD be called before Report calls, but MAY NOT because
   134  	// of goroutine races.
   135  	// In case of ctx cancel, SHOULD return nil, but MAY ctx.Err(), or error caused ctx.Err()
   136  	// in terms of github.com/pkg/errors.Cause.
   137  	// In case of any dropped Sample (unhandled because of Sample queue overflow) Run SHOULD return
   138  	// error describing how many samples were dropped.
   139  	Run(ctx context.Context, deps AggregatorDeps) error
   140  	// Report reports sample to aggregator. SHOULD be lightweight and not blocking,
   141  	// so Instance can Shoot as soon as possible.
   142  	// That means, that Sample encode and reporting SHOULD NOT be done in caller goroutine,
   143  	// but SHOULD in Aggregator Run goroutine.
   144  	// If Aggregator can't handle Reported Sample without blocking, it SHOULD just drop it.
   145  	// Reported Samples MAY just be dropped, after context cancel.
   146  	// Reported Sample MAY be reused for efficiency, so caller MUST NOT retain reference to Sample.
   147  	// Report MAY be called before Aggregator Run. Report MAY be called after Run finish, in case of
   148  	// Pool Run cancel.
   149  	// Aggregator SHOULD Return Sample if it implements BorrowedSample.
   150  	Report(s Sample)
   151  }
   152  
   153  // AggregatorDeps are passed to Aggregator in Run.
   154  // WARN: another fields could be added in next MINOR versions.
   155  // That is NOT considered as a breaking compatibility change.
   156  type AggregatorDeps struct {
   157  	Log *zap.Logger
   158  }
   159  
   160  //go:generate mockery --name=Schedule --case=underscore --outpkg=coremock
   161  
   162  // Schedule represents operation schedule. Schedule MUST be goroutine safe.
   163  type Schedule interface {
   164  	// Start starts schedule at passed time.
   165  	// Start SHOULD be called once, before any Next call.
   166  	// Start MUST NOT be called more than once or after Next call.
   167  	// If Start was not called, Schedule MUST be started on first Next call.
   168  	Start(startAt time.Time)
   169  
   170  	// Next withdraw one operation token and returns next operation time and
   171  	// ok equal true, when Schedule is not finished.
   172  	// If there is no operation tokens left, Next returns Schedule finish time and ok equals false.
   173  	// If Next called first time and Start was not called, Schedule MUST start and return tx
   174  	// equal to start time.
   175  	// Returned ts values MUST increase monotonically. That is, ts returned on next Next call MUST
   176  	// be greater or equal than returned on previous.
   177  	Next() (ts time.Time, ok bool)
   178  
   179  	// Left returns n >= 0 number operation token left, if it is known exactly.
   180  	// Returns n < 0, if number of operation tokens is unknown.
   181  	// Left MAY be called before Start.
   182  	Left() int
   183  }
   184  
   185  //go:generate mockery --name=DataSource --case=underscore --outpkg=coremock
   186  
   187  // DataSource is abstract, ready to only open, source of data.
   188  // Returned source MUST implement io.ReadCloser at least, but can implement more wide interface,
   189  // and this interface methods MAY be used. For example, returned source can be afero.File,
   190  // and can be seeked in such case.
   191  // Examples:
   192  // Dummy os.Stdin wrapper.
   193  // File DataSource that contains filename and afero.Fs, and returns afero.File on OpenSource.
   194  // HTTP DataSource that contains URL and headers used on OpenSource to download content to file,
   195  // and return afero.File, that will be deleted on rc Close.
   196  // String DataSource returns just wrapped *bytes.Buffer with string content.
   197  type DataSource interface {
   198  	// OpenSource opens source for read. OpenSource MUST NOT be called more than once.
   199  	// Returned rc SHOULD have low latency and good enough throughput for Read.
   200  	// rc MAY be afero.File but SHOULD NOT be TCP connection for example.
   201  	// DataSource MAY be some remote resource, but OpenSource SHOULD download all necessary data to
   202  	// local temporary file and return it as rc.
   203  	// Rule of thumb: returned rc SHOULD be afero.File or wrapped *bytes.Buffer.
   204  	// Returned rc SHOULD cleanup all created temporary resources on Close.
   205  	// rc owner SHOULD NOT try cast it to concrete types. For example, rc can be
   206  	// wrapped temporary *os.File, that will be deleted on Close, so it can't be casted to *os.File,
   207  	// because has type of wrapper, but can be used as afero.File.
   208  	// rc Reads SHOULD be buffered for better performance if it doesn't implement io.ByteReader.
   209  	// That usually means that short Reads are efficient enough. It is implemented by *bytes.Buffer
   210  	// and *bufio.Reader, for example. rc Reads MAY be buffered regardless.
   211  	OpenSource() (rc io.ReadCloser, err error)
   212  }
   213  
   214  //go:generate mockery --name=DataSink --case=underscore --outpkg=coremock
   215  
   216  // DataSink is abstract ready to open sink of data.
   217  //
   218  // Examples:
   219  // Dummy os.Stdout wrapper.
   220  // File DataSink that contains filename and afero.Fs, and returns afero.File on OpenSource.
   221  // HTTP DataSink  caches Written data to temporary file on wc Writes,
   222  // and POST it using contained URL and headers on wc Close.
   223  type DataSink interface {
   224  	// OpenSink opens sink for writing. OpenSink MUST NOT be called more than once.
   225  	// Returned wc SHOULD have low latency and good enough throughput for Write.
   226  	// wc MAY be afero.File but SHOULD NOT be TCP connection for example.
   227  	// DataSink MAY upload Wrote data somewhere but SHOULD do it on wc Close or in background
   228  	// goroutine.
   229  	// wc Writes SHOULD be buffered for better performance if it doesn't implement io.ByteWriter.
   230  	// That usually means that short Writes are efficient enough. It is implemented by *bytes.Buffer
   231  	// and *bufio.Writer, for example. wc Writes MAY be buffered regardless.
   232  	OpenSink() (wc io.WriteCloser, err error)
   233  }