github.com/yandex/pandora@v0.5.32/core/engine/instance.go (about) 1 package engine 2 3 import ( 4 "context" 5 "io" 6 7 "github.com/pkg/errors" 8 "github.com/yandex/pandora/core" 9 "github.com/yandex/pandora/core/aggregator/netsample" 10 "github.com/yandex/pandora/core/coreutil" 11 "github.com/yandex/pandora/lib/tag" 12 "go.uber.org/zap" 13 ) 14 15 type instance struct { 16 log *zap.Logger 17 id int 18 gun core.Gun 19 schedule core.Schedule 20 instanceSharedDeps 21 } 22 23 func newInstance(ctx context.Context, log *zap.Logger, poolID string, id int, deps instanceDeps) (*instance, error) { 24 log = log.With(zap.Int("instance", id)) 25 gunDeps := core.GunDeps{Ctx: ctx, Log: log, PoolID: poolID, InstanceID: id, Shared: deps.gunDeps} 26 sched, err := deps.newSchedule() 27 if err != nil { 28 return nil, err 29 } 30 gun, err := deps.newGun() 31 if err != nil { 32 return nil, err 33 } 34 35 err = gun.Bind(deps.aggregator, gunDeps) 36 if err != nil { 37 return nil, err 38 } 39 inst := &instance{log: log, id: id, gun: gun, schedule: sched, instanceSharedDeps: deps.instanceSharedDeps} 40 return inst, nil 41 } 42 43 type instanceDeps struct { 44 newSchedule func() (core.Schedule, error) 45 newGun func() (core.Gun, error) 46 instanceSharedDeps 47 } 48 49 type instanceSharedDeps struct { 50 provider core.Provider 51 metrics Metrics 52 gunDeps any 53 aggregator core.Aggregator 54 discardOverflow bool 55 } 56 57 // Run blocks until ammo finish, error or context cancel. 58 // Expects, that gun is already bind. 59 func (i *instance) Run(ctx context.Context) (recoverErr error) { 60 defer func() { 61 r := recover() 62 if r != nil { 63 recoverErr = errors.Errorf("shoot panic: %s", r) 64 } 65 66 i.log.Debug("Instance finished") 67 i.metrics.InstanceFinish.Add(1) 68 }() 69 i.log.Debug("Instance started") 70 i.metrics.InstanceStart.Add(1) 71 72 waiter := coreutil.NewWaiter(i.schedule) 73 // Checking, that schedule is not finished, required, to not consume extra ammo, 74 // on finish in case of per instance schedule. 75 for !waiter.IsFinished(ctx) { 76 err := func() error { 77 ammo, ok := i.provider.Acquire() 78 if !ok { 79 i.log.Debug("Out of ammo") 80 return outOfAmmoErr 81 } 82 defer i.provider.Release(ammo) 83 if tag.Debug { 84 i.log.Debug("Ammo acquired", zap.Any("ammo", ammo)) 85 } 86 if !waiter.Wait(ctx) { 87 return nil 88 } 89 i.metrics.Request.Add(1) 90 if !i.discardOverflow || !waiter.IsSlowDown(ctx) { 91 i.metrics.BusyInstances.OnStart(i.id) 92 defer i.metrics.BusyInstances.OnFinish(i.id) 93 if tag.Debug { 94 i.log.Debug("Shooting", zap.Any("ammo", ammo)) 95 } 96 i.gun.Shoot(ammo) 97 } else { 98 i.aggregator.Report(netsample.DiscardedShootSample()) 99 } 100 i.metrics.Response.Add(1) 101 return nil 102 }() 103 if err != nil { 104 return err 105 } 106 } 107 return ctx.Err() 108 } 109 110 func (i *instance) Close() error { 111 gunCloser, ok := i.gun.(io.Closer) 112 if !ok { 113 return nil 114 } 115 err := gunCloser.Close() 116 if err != nil { 117 i.log.Warn("Gun close fail", zap.Error(err)) 118 } 119 i.log.Debug("Gun closed") 120 return err 121 } 122 123 var outOfAmmoErr = errors.New("Out of ammo")