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  }