3
3
// license that can be found in the LICENSE file.
4
4
5
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
- The key is used to look up the function so it can be called later.
13
- var laterFunc = delay.Func("key", myFunc)
14
- It is also possible to use a function literal.
15
- var laterFunc = delay.Func("key", func(c context.Context, x string) {
16
- // ...
17
- })
18
-
19
- To call a function, invoke its Call method.
20
- laterFunc.Call(c, "something")
21
- A function may be called any number of times. If the function has any
22
- return arguments, and the last one is of type error, the function may
23
- return a non-nil error to signal that the function should be retried.
24
-
25
- The arguments to functions may be of any type that is encodable by the gob
26
- package. If an argument is of interface type, it is the client's responsibility
27
- to register with the gob package whatever concrete type may be passed for that
28
- argument; see http://golang.org/pkg/gob/#Register for details.
29
-
30
- Any errors during initialization or execution of a function will be
31
- logged to the application logs. Error logs that occur during initialization will
32
- be associated with the request that invoked the Call method.
33
-
34
- The state of a function invocation that has not yet successfully
35
- executed is preserved by combining the file name in which it is declared
36
- with the string key that was passed to the Func function. Updating an app
37
- with pending function invocations should safe as long as the relevant
38
- functions have the (filename, key) combination preserved. The filename is
39
- parsed according to these rules:
40
- * Paths in package main are shortened to just the file name (github.com/foo/foo.go -> foo.go)
41
- * Paths are stripped to just package paths (/go/src/github.com/foo/bar.go -> github.com/foo/bar.go)
42
- * Module versions are stripped (/go/pkg/mod/github.com/foo/[email protected] /baz.go -> github.com/foo/bar/baz.go)
43
-
44
- There is some inherent risk of pending function invocations being lost during
45
- an update that contains large changes. For example, switching from using GOPATH
46
- to go.mod is a large change that may inadvertently cause file paths to change.
47
-
48
- The delay package uses the Task Queue API to create tasks that call the
49
- reserved application path "/_ah/queue/go/delay".
50
- This path must not be marked as "login: required" in app.yaml;
51
- it must be marked as "login: admin" or have no access restriction.
6
+ Package delay provides a way to execute code outside of the scope of
7
+ a user request by using the Task Queue API.
8
+ To use a deferred function, you must register the function to be
9
+ deferred as a top-level var. For example,
10
+ ```
11
+ var laterFunc = delay.MustRegister("key", myFunc)
12
+ func myFunc(ctx context.Context, a, b string) {...}
13
+ ```
14
+ You can also inline with a function literal:
15
+ ```
16
+ var laterFunc = delay.MustRegister("key", func(ctx context.Context, a, b string) {...})
17
+ ```
18
+ In the above example, "key" is a logical name for the function.
19
+ The key needs to be globally unique across your entire application.
20
+ To invoke the function in a deferred fashion, call the top-level item:
21
+ ```
22
+ laterFunc(ctx, "aaa", "bbb")
23
+ ```
24
+ This will queue a task and return quickly; the function will be actually
25
+ run in a new request. The delay package uses the Task Queue API to create
26
+ tasks that call the reserved application path "/_ah/queue/go/delay".
27
+ This path may only be marked as "login: admin" or have no access
28
+ restriction; it will fail if marked as "login: required".
52
29
*/
53
30
package delay // import "google.golang.org/appengine/v2/delay"
54
31
@@ -151,34 +128,72 @@ func fileKey(file string) (string, error) {
151
128
return modVersionPat .ReplaceAllString (file , "" ), nil
152
129
}
153
130
154
- // Func declares a new Function. The second argument must be a function with a
155
- // first argument of type context.Context.
156
- // This function must be called at program initialization time. That means it
157
- // must be called in a global variable declaration or from an init function.
158
- // This restriction is necessary because the instance that delays a function
159
- // call may not be the one that executes it. Only the code executed at program
160
- // initialization time is guaranteed to have been run by an instance before it
161
- // receives a request.
131
+ // Func declares a new function that can be called in a deferred fashion.
132
+ // The second argument i must be a function with the first argument of
133
+ // type context.Context.
134
+ // To make the key globally unique, the SDK code will combine "key" with
135
+ // the filename of the file in which myFunc is defined
136
+ // (e.g., /some/path/myfile.go). This is convenient, but can lead to
137
+ // failed deferred tasks if you refactor your code, or change from
138
+ // GOPATH to go.mod, and then re-deploy with in-flight deferred tasks.
139
+ //
140
+ // This function Func must be called in a global scope to properly
141
+ // register the function with the framework.
142
+ //
143
+ // Deprecated: Use MustRegister instead.
162
144
func Func (key string , i interface {}) * Function {
163
- f := & Function {fv : reflect .ValueOf (i )}
164
-
165
145
// Derive unique, somewhat stable key for this func.
166
146
_ , file , _ , _ := runtime .Caller (1 )
167
147
fk , err := fileKey (file )
168
148
if err != nil {
169
149
// Not fatal, but log the error
170
150
stdlog .Printf ("delay: %v" , err )
171
151
}
172
- f .key = fk + ":" + key
152
+ key = fk + ":" + key
153
+ f , err := registerFunction (key , i )
154
+ if err != nil {
155
+ return f
156
+ }
157
+ if old := funcs [f .key ]; old != nil {
158
+ old .err = fmt .Errorf ("multiple functions registered for %s" , key )
159
+ }
160
+ funcs [f .key ] = f
161
+ return f
162
+ }
163
+
164
+ // MustRegister declares a new function that can be called in a deferred fashion.
165
+ // The second argument i must be a function with the first argument of
166
+ // type context.Context.
167
+ // MustRegister requires the key to be globally unique.
168
+ //
169
+ // This function MustRegister must be called in a global scope to properly
170
+ // register the function with the framework.
171
+ // See the package notes above for more details.
172
+ func MustRegister (key string , i interface {}) * Function {
173
+ f , err := registerFunction (key , i )
174
+ if err != nil {
175
+ panic (err )
176
+ }
177
+
178
+ if old := funcs [f .key ]; old != nil {
179
+ panic (fmt .Errorf ("multiple functions registered for %q" , key ))
180
+ }
181
+ funcs [f .key ] = f
182
+ return f
183
+ }
184
+
185
+ func registerFunction (key string , i interface {}) (* Function , error ) {
186
+ f := & Function {fv : reflect .ValueOf (i )}
187
+ f .key = key
173
188
174
189
t := f .fv .Type ()
175
190
if t .Kind () != reflect .Func {
176
191
f .err = errors .New ("not a function" )
177
- return f
192
+ return f , f . err
178
193
}
179
194
if t .NumIn () == 0 || ! isContext (t .In (0 )) {
180
195
f .err = errFirstArg
181
- return f
196
+ return f , errFirstArg
182
197
}
183
198
184
199
// Register the function's arguments with the gob package.
@@ -194,12 +209,7 @@ func Func(key string, i interface{}) *Function {
194
209
}
195
210
gob .Register (reflect .Zero (t .In (i )).Interface ())
196
211
}
197
-
198
- if old := funcs [f .key ]; old != nil {
199
- old .err = fmt .Errorf ("multiple functions registered for %s in %s" , key , file )
200
- }
201
- funcs [f .key ] = f
202
- return f
212
+ return f , nil
203
213
}
204
214
205
215
type invocation struct {
0 commit comments