Skip to content

Commit 0b066d6

Browse files
authored
Fix schedule function deployment (#1305)
* fixing retry config for scheduled functions * change task queue behavior and clean up things * small cleanup * removing unnecessary '|| {}' from copyIfPresent * move preserveExternalChanges to runtime options
1 parent efc160a commit 0b066d6

14 files changed

+368
-44
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
- Deprecate typoed function name lessThanorEqualTo (#1284)
2+
- Fix a bug where supplying preserveExternalChanges to scheduled functions would cause deployment failure (#1305).

spec/runtime/manifest.spec.ts

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { expect } from "chai";
2-
import { stackToWire, ManifestStack } from "../../src/runtime/manifest";
2+
import {
3+
stackToWire,
4+
ManifestStack,
5+
initV2ScheduleTrigger,
6+
initV1ScheduleTrigger,
7+
initTaskQueueTrigger,
8+
} from "../../src/runtime/manifest";
9+
import { RESET_VALUE } from "../../src/common/options";
310
import * as params from "../../src/params";
411
import * as optsv2 from "../../src/v2/options";
512
import * as v1 from "../../src/v1";
13+
import { DeploymentOptions } from "../../src/v1";
614

715
describe("stackToWire", () => {
816
afterEach(() => {
@@ -168,3 +176,90 @@ describe("stackToWire", () => {
168176
expect(stackToWire(stack)).to.deep.equal(expected);
169177
});
170178
});
179+
180+
describe("initTaskQueueTrigger", () => {
181+
it("should init a taskQueueTrigger without preserveExternalChanges", () => {
182+
const tt = initTaskQueueTrigger();
183+
184+
expect(tt).to.deep.eq({
185+
retryConfig: {
186+
maxAttempts: RESET_VALUE,
187+
maxDoublings: RESET_VALUE,
188+
maxBackoffSeconds: RESET_VALUE,
189+
maxRetrySeconds: RESET_VALUE,
190+
minBackoffSeconds: RESET_VALUE,
191+
},
192+
rateLimits: {
193+
maxConcurrentDispatches: RESET_VALUE,
194+
maxDispatchesPerSecond: RESET_VALUE,
195+
},
196+
});
197+
});
198+
199+
it("should init a taskQueueTrigger with preserveExternalChanges", () => {
200+
const opts: DeploymentOptions = { preserveExternalChanges: true };
201+
202+
const tt = initTaskQueueTrigger(opts);
203+
204+
expect(tt).to.deep.eq({
205+
rateLimits: {},
206+
retryConfig: {},
207+
});
208+
});
209+
});
210+
211+
describe("initScheduleTrigger", () => {
212+
it("should init a v1 scheduleTrigger without preserveExternalChanges", () => {
213+
const st = initV1ScheduleTrigger("every 30 minutes");
214+
215+
expect(st).to.deep.eq({
216+
schedule: "every 30 minutes",
217+
timeZone: RESET_VALUE,
218+
retryConfig: {
219+
retryCount: RESET_VALUE,
220+
maxDoublings: RESET_VALUE,
221+
maxRetryDuration: RESET_VALUE,
222+
minBackoffDuration: RESET_VALUE,
223+
maxBackoffDuration: RESET_VALUE,
224+
},
225+
});
226+
});
227+
228+
it("should init a v1 scheduleTrigger with preserveExternalChanges", () => {
229+
const opts: DeploymentOptions = { preserveExternalChanges: true };
230+
231+
const st = initV1ScheduleTrigger("every 30 minutes", opts);
232+
233+
expect(st).to.deep.eq({
234+
schedule: "every 30 minutes",
235+
retryConfig: {},
236+
});
237+
});
238+
239+
it("should init a v2 scheduleTrigger without preserveExternalChanges", () => {
240+
const st = initV2ScheduleTrigger("every 30 minutes");
241+
242+
expect(st).to.deep.eq({
243+
schedule: "every 30 minutes",
244+
timeZone: RESET_VALUE,
245+
retryConfig: {
246+
retryCount: RESET_VALUE,
247+
maxDoublings: RESET_VALUE,
248+
maxRetrySeconds: RESET_VALUE,
249+
minBackoffSeconds: RESET_VALUE,
250+
maxBackoffSeconds: RESET_VALUE,
251+
},
252+
});
253+
});
254+
255+
it("should init a v2 scheduleTrigger with preserveExternalChanges", () => {
256+
const opts: DeploymentOptions = { preserveExternalChanges: true };
257+
258+
const st = initV2ScheduleTrigger("every 30 minutes", opts);
259+
260+
expect(st).to.deep.eq({
261+
schedule: "every 30 minutes",
262+
retryConfig: {},
263+
});
264+
});
265+
});

spec/v1/cloud-functions.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,35 @@ describe("makeCloudFunction", () => {
189189
});
190190
});
191191

192+
it("should setup a scheduleTrigger in __endpoint given a schedule and preserveExternalChanges", () => {
193+
const schedule = {
194+
schedule: "every 5 minutes",
195+
retryConfig: { retryCount: 3 },
196+
timeZone: "America/New_York",
197+
};
198+
const cf = makeCloudFunction({
199+
provider: "mock.provider",
200+
eventType: "mock.event",
201+
service: "service",
202+
triggerResource: () => "resource",
203+
handler: () => null,
204+
options: {
205+
schedule,
206+
preserveExternalChanges: true,
207+
},
208+
});
209+
expect(cf.__endpoint).to.deep.equal({
210+
platform: "gcfv1",
211+
scheduleTrigger: {
212+
...schedule,
213+
retryConfig: {
214+
...schedule.retryConfig,
215+
},
216+
},
217+
labels: {},
218+
});
219+
});
220+
192221
it("should construct the right context for event", () => {
193222
const args: any = {
194223
...cloudFunctionArgs,

spec/v1/providers/pubsub.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,32 @@ describe("Pubsub Functions", () => {
388388
expect(result.__endpoint.availableMemoryMb).to.deep.equal(256);
389389
expect(result.__endpoint.timeoutSeconds).to.deep.equal(90);
390390
});
391+
392+
it("should return an appropriate endpoint when called with preserveExternalChanges", () => {
393+
const result = functions
394+
.region("us-east1")
395+
.runWith({
396+
timeoutSeconds: 90,
397+
memory: "256MB",
398+
preserveExternalChanges: true,
399+
})
400+
.pubsub.schedule("every 5 minutes")
401+
.timeZone("America/New_York")
402+
.onRun(() => null);
403+
404+
expect(result.__endpoint).to.deep.eq({
405+
platform: "gcfv1",
406+
labels: {},
407+
region: ["us-east1"],
408+
availableMemoryMb: 256,
409+
timeoutSeconds: 90,
410+
scheduleTrigger: {
411+
schedule: "every 5 minutes",
412+
timeZone: "America/New_York",
413+
retryConfig: {},
414+
},
415+
});
416+
});
391417
});
392418
});
393419

spec/v1/providers/tasks.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { MockRequest } from "../../fixtures/mockrequest";
2828
import { runHandler } from "../../helper";
2929
import { MINIMAL_V1_ENDPOINT } from "../../fixtures";
3030
import { MINIMIAL_TASK_QUEUE_TRIGGER } from "./fixtures";
31+
import { runWith } from "../../../src/v1";
3132

3233
describe("#onDispatch", () => {
3334
it("should return a trigger/endpoint with appropriate values", () => {
@@ -67,6 +68,7 @@ describe("#onDispatch", () => {
6768
...MINIMAL_V1_ENDPOINT,
6869
platform: "gcfv1",
6970
taskQueueTrigger: {
71+
...MINIMIAL_TASK_QUEUE_TRIGGER,
7072
rateLimits: {
7173
maxConcurrentDispatches: 30,
7274
maxDispatchesPerSecond: 40,
@@ -83,6 +85,35 @@ describe("#onDispatch", () => {
8385
});
8486
});
8587

88+
it("should return an endpoint with appropriate values with preserveExternalChanges set", () => {
89+
const result = runWith({ preserveExternalChanges: true })
90+
.tasks.taskQueue({
91+
rateLimits: {
92+
maxConcurrentDispatches: 30,
93+
},
94+
retryConfig: {
95+
maxAttempts: 5,
96+
maxRetrySeconds: 10,
97+
},
98+
invoker: "private",
99+
})
100+
.onDispatch(() => undefined);
101+
102+
expect(result.__endpoint).to.deep.equal({
103+
platform: "gcfv1",
104+
taskQueueTrigger: {
105+
rateLimits: {
106+
maxConcurrentDispatches: 30,
107+
},
108+
retryConfig: {
109+
maxAttempts: 5,
110+
maxRetrySeconds: 10,
111+
},
112+
invoker: ["private"],
113+
},
114+
});
115+
});
116+
86117
it("should allow both region and runtime options to be set", () => {
87118
const fn = functions
88119
.region("us-east1")
@@ -114,6 +145,10 @@ describe("#onDispatch", () => {
114145
...MINIMIAL_TASK_QUEUE_TRIGGER,
115146
retryConfig: {
116147
maxAttempts: 5,
148+
maxBackoffSeconds: functions.RESET_VALUE,
149+
maxDoublings: functions.RESET_VALUE,
150+
maxRetrySeconds: functions.RESET_VALUE,
151+
minBackoffSeconds: functions.RESET_VALUE,
117152
},
118153
},
119154
});

spec/v2/providers/scheduler.spec.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,13 @@ describe("schedule", () => {
6363
expect(schedule.getOpts(options)).to.deep.eq({
6464
schedule: "* * * * *",
6565
timeZone: "utc",
66-
retryCount: 3,
67-
maxRetrySeconds: 1,
68-
minBackoffSeconds: 2,
69-
maxBackoffSeconds: 3,
70-
maxDoublings: 4,
66+
retryConfig: {
67+
retryCount: 3,
68+
maxRetrySeconds: 1,
69+
minBackoffSeconds: 2,
70+
maxBackoffSeconds: 3,
71+
maxDoublings: 4,
72+
},
7173
opts: {
7274
...options,
7375
memory: "128MiB",
@@ -139,6 +141,38 @@ describe("schedule", () => {
139141
]);
140142
});
141143

144+
it("should create a schedule function with preserveExternalChanges", () => {
145+
const schfn = schedule.onSchedule(
146+
{
147+
schedule: "* * * * *",
148+
preserveExternalChanges: true,
149+
},
150+
() => console.log(1)
151+
);
152+
153+
expect(schfn.__endpoint).to.deep.eq({
154+
platform: "gcfv2",
155+
labels: {},
156+
scheduleTrigger: {
157+
schedule: "* * * * *",
158+
timeZone: undefined,
159+
retryConfig: {
160+
retryCount: undefined,
161+
maxRetrySeconds: undefined,
162+
minBackoffSeconds: undefined,
163+
maxBackoffSeconds: undefined,
164+
maxDoublings: undefined,
165+
},
166+
},
167+
});
168+
expect(schfn.__requiredAPIs).to.deep.eq([
169+
{
170+
api: "cloudscheduler.googleapis.com",
171+
reason: "Needed for scheduled functions.",
172+
},
173+
]);
174+
});
175+
142176
it("should have a .run method", async () => {
143177
const testObj = {
144178
foo: "bar",

spec/v2/providers/tasks.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,73 @@ describe("onTaskDispatched", () => {
128128
});
129129
});
130130

131+
it("should return a minimal endpoint without preserveExternalChanges set", () => {
132+
const result = onTaskDispatched(
133+
{
134+
retryConfig: {
135+
maxAttempts: 4,
136+
maxRetrySeconds: 10,
137+
},
138+
rateLimits: {
139+
maxDispatchesPerSecond: 10,
140+
},
141+
},
142+
() => undefined
143+
);
144+
145+
expect(result.__endpoint).to.deep.equal({
146+
...MINIMAL_V2_ENDPOINT,
147+
platform: "gcfv2",
148+
labels: {},
149+
taskQueueTrigger: {
150+
retryConfig: {
151+
maxAttempts: 4,
152+
maxRetrySeconds: 10,
153+
maxBackoffSeconds: options.RESET_VALUE,
154+
maxDoublings: options.RESET_VALUE,
155+
minBackoffSeconds: options.RESET_VALUE,
156+
},
157+
rateLimits: {
158+
maxDispatchesPerSecond: 10,
159+
maxConcurrentDispatches: options.RESET_VALUE,
160+
},
161+
},
162+
});
163+
});
164+
165+
it("should create a complex endpoint with preserveExternalChanges set", () => {
166+
const result = onTaskDispatched(
167+
{
168+
...FULL_OPTIONS,
169+
retryConfig: {
170+
maxAttempts: 4,
171+
maxRetrySeconds: 10,
172+
},
173+
rateLimits: {
174+
maxDispatchesPerSecond: 10,
175+
},
176+
invoker: "private",
177+
preserveExternalChanges: true,
178+
},
179+
() => undefined
180+
);
181+
182+
expect(result.__endpoint).to.deep.equal({
183+
...FULL_ENDPOINT,
184+
platform: "gcfv2",
185+
taskQueueTrigger: {
186+
retryConfig: {
187+
maxAttempts: 4,
188+
maxRetrySeconds: 10,
189+
},
190+
rateLimits: {
191+
maxDispatchesPerSecond: 10,
192+
},
193+
invoker: ["private"],
194+
},
195+
});
196+
});
197+
131198
it("should merge options and globalOptions", () => {
132199
options.setGlobalOptions({
133200
concurrency: 20,

spec/v2/providers/testLab.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import { expect } from "chai";
2424
import * as testLab from "../../../src/v2/providers/testLab";
2525
import * as options from "../../../src/v2/options";
26+
import { MINIMAL_V2_ENDPOINT } from "../../fixtures";
2627

2728
describe("onTestMatrixCompleted", () => {
2829
afterEach(() => {
@@ -33,6 +34,7 @@ describe("onTestMatrixCompleted", () => {
3334
const fn = testLab.onTestMatrixCompleted(() => 2);
3435

3536
expect(fn.__endpoint).to.deep.eq({
37+
...MINIMAL_V2_ENDPOINT,
3638
platform: "gcfv2",
3739
labels: {},
3840
eventTrigger: {
@@ -59,6 +61,7 @@ describe("onTestMatrixCompleted", () => {
5961
);
6062

6163
expect(fn.__endpoint).to.deep.eq({
64+
...MINIMAL_V2_ENDPOINT,
6265
platform: "gcfv2",
6366
availableMemoryMb: 512,
6467
region: ["us-central1"],

0 commit comments

Comments
 (0)