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 }