github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/delay/delay.go (about)

     1  // Copyright 2011 Google Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Package delay provides a way to execute code outside the scope of a
     7  user request by using the taskqueue API.
     8  
     9  To declare a function that may be executed later, call Func
    10  in a top-level assignment context, passing it an arbitrary string key
    11  and a function whose first argument is of type context.Context.
    12  	var laterFunc = delay.Func("key", myFunc)
    13  It is also possible to use a function literal.
    14  	var laterFunc = delay.Func("key", func(c context.Context, x string) {
    15  		// ...
    16  	})
    17  
    18  To call a function, invoke its Call method.
    19  	laterFunc.Call(c, "something")
    20  A function may be called any number of times. If the function has any
    21  return arguments, and the last one is of type error, the function may
    22  return a non-nil error to signal that the function should be retried.
    23  
    24  The arguments to functions may be of any type that is encodable by the gob
    25  package. If an argument is of interface type, it is the client's responsibility
    26  to register with the gob package whatever concrete type may be passed for that
    27  argument; see http://golang.org/pkg/gob/#Register for details.
    28  
    29  Any errors during initialization or execution of a function will be
    30  logged to the application logs. Error logs that occur during initialization will
    31  be associated with the request that invoked the Call method.
    32  
    33  The state of a function invocation that has not yet successfully
    34  executed is preserved by combining the file name in which it is declared
    35  with the string key that was passed to the Func function. Updating an app
    36  with pending function invocations is safe as long as the relevant
    37  functions have the (filename, key) combination preserved.
    38  
    39  The delay package uses the Task Queue API to create tasks that call the
    40  reserved application path "/_ah/queue/go/delay".
    41  This path must not be marked as "login: required" in app.yaml;
    42  it must be marked as "login: admin" or have no access restriction.
    43  */
    44  package delay
    45  
    46  import (
    47  	"bytes"
    48  	"encoding/gob"
    49  	"errors"
    50  	"fmt"
    51  	"net/http"
    52  	"reflect"
    53  	"runtime"
    54  
    55  	"golang.org/x/net/context"
    56  
    57  	"google.golang.org/appengine"
    58  	"google.golang.org/appengine/log"
    59  	"google.golang.org/appengine/taskqueue"
    60  )
    61  
    62  // Function represents a function that may have a delayed invocation.
    63  type Function struct {
    64  	fv  reflect.Value // Kind() == reflect.Func
    65  	key string
    66  	err error // any error during initialization
    67  }
    68  
    69  const (
    70  	// The HTTP path for invocations.
    71  	path = "/_ah/queue/go/delay"
    72  	// Use the default queue.
    73  	queue = ""
    74  )
    75  
    76  var (
    77  	// registry of all delayed functions
    78  	funcs = make(map[string]*Function)
    79  
    80  	// precomputed types
    81  	contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
    82  	errorType   = reflect.TypeOf((*error)(nil)).Elem()
    83  
    84  	// errors
    85  	errFirstArg = errors.New("first argument must be context.Context")
    86  )
    87  
    88  // Func declares a new Function. The second argument must be a function with a
    89  // first argument of type context.Context.
    90  // This function must be called at program initialization time. That means it
    91  // must be called in a global variable declaration or from an init function.
    92  // This restriction is necessary because the instance that delays a function
    93  // call may not be the one that executes it. Only the code executed at program
    94  // initialization time is guaranteed to have been run by an instance before it
    95  // receives a request.
    96  func Func(key string, i interface{}) *Function {
    97  	f := &Function{fv: reflect.ValueOf(i)}
    98  
    99  	// Derive unique, somewhat stable key for this func.
   100  	_, file, _, _ := runtime.Caller(1)
   101  	f.key = file + ":" + key
   102  
   103  	t := f.fv.Type()
   104  	if t.Kind() != reflect.Func {
   105  		f.err = errors.New("not a function")
   106  		return f
   107  	}
   108  	if t.NumIn() == 0 || t.In(0) != contextType {
   109  		f.err = errFirstArg
   110  		return f
   111  	}
   112  
   113  	// Register the function's arguments with the gob package.
   114  	// This is required because they are marshaled inside a []interface{}.
   115  	// gob.Register only expects to be called during initialization;
   116  	// that's fine because this function expects the same.
   117  	for i := 0; i < t.NumIn(); i++ {
   118  		// Only concrete types may be registered. If the argument has
   119  		// interface type, the client is resposible for registering the
   120  		// concrete types it will hold.
   121  		if t.In(i).Kind() == reflect.Interface {
   122  			continue
   123  		}
   124  		gob.Register(reflect.Zero(t.In(i)).Interface())
   125  	}
   126  
   127  	funcs[f.key] = f
   128  	return f
   129  }
   130  
   131  type invocation struct {
   132  	Key  string
   133  	Args []interface{}
   134  }
   135  
   136  // Call invokes a delayed function.
   137  //   err := f.Call(c, ...)
   138  // is equivalent to
   139  //   t, _ := f.Task(...)
   140  //   err := taskqueue.Add(c, t, "")
   141  func (f *Function) Call(c context.Context, args ...interface{}) error {
   142  	t, err := f.Task(args...)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	_, err = taskqueueAdder(c, t, queue)
   147  	return err
   148  }
   149  
   150  // Task creates a Task that will invoke the function.
   151  // Its parameters may be tweaked before adding it to a queue.
   152  // Users should not modify the Path or Payload fields of the returned Task.
   153  func (f *Function) Task(args ...interface{}) (*taskqueue.Task, error) {
   154  	if f.err != nil {
   155  		return nil, fmt.Errorf("delay: func is invalid: %v", f.err)
   156  	}
   157  
   158  	nArgs := len(args) + 1 // +1 for the context.Context
   159  	ft := f.fv.Type()
   160  	minArgs := ft.NumIn()
   161  	if ft.IsVariadic() {
   162  		minArgs--
   163  	}
   164  	if nArgs < minArgs {
   165  		return nil, fmt.Errorf("delay: too few arguments to func: %d < %d", nArgs, minArgs)
   166  	}
   167  	if !ft.IsVariadic() && nArgs > minArgs {
   168  		return nil, fmt.Errorf("delay: too many arguments to func: %d > %d", nArgs, minArgs)
   169  	}
   170  
   171  	// Check arg types.
   172  	for i := 1; i < nArgs; i++ {
   173  		at := reflect.TypeOf(args[i-1])
   174  		var dt reflect.Type
   175  		if i < minArgs {
   176  			// not a variadic arg
   177  			dt = ft.In(i)
   178  		} else {
   179  			// a variadic arg
   180  			dt = ft.In(minArgs).Elem()
   181  		}
   182  		// nil arguments won't have a type, so they need special handling.
   183  		if at == nil {
   184  			// nil interface
   185  			switch dt.Kind() {
   186  			case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   187  				continue // may be nil
   188  			}
   189  			return nil, fmt.Errorf("delay: argument %d has wrong type: %v is not nilable", i, dt)
   190  		}
   191  		switch at.Kind() {
   192  		case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   193  			av := reflect.ValueOf(args[i-1])
   194  			if av.IsNil() {
   195  				// nil value in interface; not supported by gob, so we replace it
   196  				// with a nil interface value
   197  				args[i-1] = nil
   198  			}
   199  		}
   200  		if !at.AssignableTo(dt) {
   201  			return nil, fmt.Errorf("delay: argument %d has wrong type: %v is not assignable to %v", i, at, dt)
   202  		}
   203  	}
   204  
   205  	inv := invocation{
   206  		Key:  f.key,
   207  		Args: args,
   208  	}
   209  
   210  	buf := new(bytes.Buffer)
   211  	if err := gob.NewEncoder(buf).Encode(inv); err != nil {
   212  		return nil, fmt.Errorf("delay: gob encoding failed: %v", err)
   213  	}
   214  
   215  	return &taskqueue.Task{
   216  		Path:    path,
   217  		Payload: buf.Bytes(),
   218  	}, nil
   219  }
   220  
   221  var taskqueueAdder = taskqueue.Add // for testing
   222  
   223  func init() {
   224  	http.HandleFunc(path, func(w http.ResponseWriter, req *http.Request) {
   225  		runFunc(appengine.NewContext(req), w, req)
   226  	})
   227  }
   228  
   229  func runFunc(c context.Context, w http.ResponseWriter, req *http.Request) {
   230  	defer req.Body.Close()
   231  
   232  	var inv invocation
   233  	if err := gob.NewDecoder(req.Body).Decode(&inv); err != nil {
   234  		log.Errorf(c, "delay: failed decoding task payload: %v", err)
   235  		log.Warningf(c, "delay: dropping task")
   236  		return
   237  	}
   238  
   239  	f := funcs[inv.Key]
   240  	if f == nil {
   241  		log.Errorf(c, "delay: no func with key %q found", inv.Key)
   242  		log.Warningf(c, "delay: dropping task")
   243  		return
   244  	}
   245  
   246  	ft := f.fv.Type()
   247  	in := []reflect.Value{reflect.ValueOf(c)}
   248  	for _, arg := range inv.Args {
   249  		var v reflect.Value
   250  		if arg != nil {
   251  			v = reflect.ValueOf(arg)
   252  		} else {
   253  			// Task was passed a nil argument, so we must construct
   254  			// the zero value for the argument here.
   255  			n := len(in) // we're constructing the nth argument
   256  			var at reflect.Type
   257  			if !ft.IsVariadic() || n < ft.NumIn()-1 {
   258  				at = ft.In(n)
   259  			} else {
   260  				at = ft.In(ft.NumIn() - 1).Elem()
   261  			}
   262  			v = reflect.Zero(at)
   263  		}
   264  		in = append(in, v)
   265  	}
   266  	out := f.fv.Call(in)
   267  
   268  	if n := ft.NumOut(); n > 0 && ft.Out(n-1) == errorType {
   269  		if errv := out[n-1]; !errv.IsNil() {
   270  			log.Errorf(c, "delay: func failed (will retry): %v", errv.Interface())
   271  			w.WriteHeader(http.StatusInternalServerError)
   272  			return
   273  		}
   274  	}
   275  }