go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/prod/taskqueue.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package prod 16 17 import ( 18 "context" 19 "fmt" 20 "reflect" 21 "time" 22 23 "google.golang.org/appengine" 24 "google.golang.org/appengine/taskqueue" 25 26 "go.chromium.org/luci/gae/impl/prod/constraints" 27 tq "go.chromium.org/luci/gae/service/taskqueue" 28 ) 29 30 // useTQ adds a gae.TaskQueue implementation to context, accessible 31 // by gae.GetTQ(c) 32 func useTQ(c context.Context) context.Context { 33 return tq.SetRawFactory(c, func(ci context.Context) tq.RawInterface { 34 return tqImpl{getAEContext(ci)} 35 }) 36 } 37 38 type tqImpl struct { 39 aeCtx context.Context 40 } 41 42 func init() { 43 const taskExpectedFields = 10 44 // Runtime-assert that the number of fields in the Task structs match, to 45 // avoid missing additional fields if they're added later. 46 // all other type assertions are statically enforced by o2n() and tqF2R() 47 48 oldType := reflect.TypeOf((*taskqueue.Task)(nil)).Elem() 49 newType := reflect.TypeOf((*tq.Task)(nil)).Elem() 50 51 if oldType.NumField() != newType.NumField() || 52 oldType.NumField() != taskExpectedFields { 53 panic(fmt.Errorf( 54 "prod/taskqueue:init() field count differs: %d, %d, %d", 55 oldType.NumField(), newType.NumField(), taskExpectedFields)) 56 } 57 } 58 59 // tqR2F (TQ real-to-fake) converts a *taskqueue.Task to a *tq.Task. 60 func tqR2F(o *taskqueue.Task) *tq.Task { 61 if o == nil { 62 return nil 63 } 64 n := tq.Task{} 65 n.Path = o.Path 66 n.Payload = o.Payload 67 n.Header = o.Header 68 n.Method = o.Method 69 n.Name = o.Name 70 n.Delay = o.Delay 71 n.ETA = o.ETA 72 n.RetryCount = o.RetryCount 73 n.RetryOptions = (*tq.RetryOptions)(o.RetryOptions) 74 return &n 75 } 76 77 // tqF2R (TQ fake-to-real) converts a *tq.Task to a *taskqueue.Task. 78 func tqF2R(n *tq.Task) *taskqueue.Task { 79 o := taskqueue.Task{} 80 o.Path = n.Path 81 o.Payload = n.Payload 82 o.Header = n.Header 83 o.Method = n.Method 84 o.Name = n.Name 85 o.Delay = n.Delay 86 o.ETA = n.ETA 87 o.RetryCount = n.RetryCount 88 o.RetryOptions = (*taskqueue.RetryOptions)(n.RetryOptions) 89 return &o 90 } 91 92 // tqMF2R (TQ multi-fake-to-real) converts []*tq.Task to []*taskqueue.Task. 93 func tqMF2R(ns []*tq.Task) []*taskqueue.Task { 94 ret := make([]*taskqueue.Task, len(ns)) 95 for i, t := range ns { 96 ret[i] = tqF2R(t) 97 } 98 return ret 99 } 100 101 // tqMR2F (TQ multi-real-to-fake) converts []*taskqueue.Task to []*tq.Task. 102 func tqMR2F(ns []*taskqueue.Task) []*tq.Task { 103 ret := make([]*tq.Task, len(ns)) 104 for i, t := range ns { 105 ret[i] = tqR2F(t) 106 } 107 return ret 108 } 109 110 func (t tqImpl) AddMulti(tasks []*tq.Task, queueName string, cb tq.RawTaskCB) error { 111 realTasks, err := taskqueue.AddMulti(t.aeCtx, tqMF2R(tasks), queueName) 112 if err != nil { 113 if me, ok := err.(appengine.MultiError); ok { 114 for i, err := range me { 115 tsk := (*taskqueue.Task)(nil) 116 if realTasks != nil { 117 tsk = realTasks[i] 118 } 119 cb(tqR2F(tsk), err) 120 } 121 err = nil 122 } 123 } else { 124 for _, tsk := range realTasks { 125 cb(tqR2F(tsk), nil) 126 } 127 } 128 return err 129 } 130 131 func (t tqImpl) DeleteMulti(tasks []*tq.Task, queueName string, cb tq.RawCB) error { 132 err := taskqueue.DeleteMulti(t.aeCtx, tqMF2R(tasks), queueName) 133 if me, ok := err.(appengine.MultiError); ok { 134 for i, err := range me { 135 if err != nil { 136 cb(i, err) 137 } 138 } 139 err = nil 140 } 141 return err 142 } 143 144 func (t tqImpl) Lease(maxTasks int, queueName string, leaseTime time.Duration) ([]*tq.Task, error) { 145 tasks, err := taskqueue.Lease(t.aeCtx, maxTasks, queueName, int(leaseTime/time.Second)) 146 if err != nil { 147 return nil, err 148 } 149 return tqMR2F(tasks), nil 150 } 151 152 func (t tqImpl) LeaseByTag(maxTasks int, queueName string, leaseTime time.Duration, tag string) ([]*tq.Task, error) { 153 tasks, err := taskqueue.LeaseByTag(t.aeCtx, maxTasks, queueName, int(leaseTime/time.Second), tag) 154 if err != nil { 155 return nil, err 156 } 157 return tqMR2F(tasks), nil 158 } 159 160 func (t tqImpl) ModifyLease(task *tq.Task, queueName string, leaseTime time.Duration) error { 161 realTask := tqF2R(task) 162 err := taskqueue.ModifyLease(t.aeCtx, realTask, queueName, int(leaseTime/time.Second)) 163 if err == nil { 164 task.ETA = realTask.ETA 165 } 166 return err 167 } 168 169 func (t tqImpl) Purge(queueName string) error { 170 return taskqueue.Purge(t.aeCtx, queueName) 171 } 172 173 func (t tqImpl) Stats(queueNames []string, cb tq.RawStatsCB) error { 174 stats, err := taskqueue.QueueStats(t.aeCtx, queueNames) 175 if err != nil { 176 return err 177 } 178 for _, s := range stats { 179 cb((*tq.Statistics)(&s), nil) 180 } 181 return nil 182 } 183 184 func (t tqImpl) Constraints() tq.Constraints { return constraints.TQ() } 185 186 func (t tqImpl) GetTestable() tq.Testable { 187 return nil 188 }