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 }