Skip to content

Commit fda97cc

Browse files
author
Guy Bedford
committed
feat: KV Store v2 (#1004)
1 parent b328546 commit fda97cc

26 files changed

+1829
-542
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ defaults:
1111
shell: bash
1212
env:
1313
# Note: when updated, also update version in ensure-cargo-installs
14-
viceroy_version: 0.12.0
14+
viceroy_version: 0.12.1
1515
# Note: when updated, also update version in ensure-cargo-installs ! AND ! release-please.yml
1616
wasm-tools_version: 1.216.0
1717
fastly-cli_version: 10.13.3
@@ -46,7 +46,7 @@ jobs:
4646
matrix:
4747
include:
4848
- crate: viceroy
49-
version: 0.12.0 # Note: workflow-level env vars can't be used in matrix definitions
49+
version: 0.12.1 # Note: workflow-level env vars can't be used in matrix definitions
5050
options: ""
5151
- crate: wasm-tools
5252
version: 1.216.0 # Note: workflow-level env vars can't be used in matrix definitions

integration-tests/js-compute/fixtures/app/src/assertions.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ export function assertDoesNotThrow(func) {
134134
}
135135
}
136136

137-
export { deepEqual as deepStrictEqual };
137+
export function deepStrictEqual(a, b) {
138+
if (!deepEqual(a, b)) {
139+
throw new Error(`Expected ${a} to equal ${b}`);
140+
}
141+
}
138142

139143
export function deepEqual(a, b) {
140144
var aKeys;
@@ -157,6 +161,15 @@ export function deepEqual(a, b) {
157161
if (b === null || typeB !== 'object') {
158162
return false;
159163
}
164+
if (Array.isArray(a) && Array.isArray(b)) {
165+
if (a.length !== b.length) return false;
166+
for (let i = 0; i < a.length; i++) {
167+
if (!deepEqual(a[i], b[i])) {
168+
return false;
169+
}
170+
}
171+
return true;
172+
}
160173
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {
161174
return false;
162175
}

integration-tests/js-compute/fixtures/app/src/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import './console.js';
1818
import './crypto.js';
1919
import './device.js';
2020
import './dictionary.js';
21-
import './dynamic-backend.js';
2221
import './edge-rate-limiter.js';
2322
import './env.js';
2423
import './fanout.js';
@@ -27,7 +26,6 @@ import './fetch-errors.js';
2726
import './geoip.js';
2827
import './headers.js';
2928
import './include-bytes.js';
30-
import './kv-store.js';
3129
import './logger.js';
3230
import './manual-framing-headers.js';
3331
import './missing-backend.js';

integration-tests/js-compute/fixtures/app/tests.json

Lines changed: 0 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -934,97 +934,6 @@
934934
"body": "ok"
935935
}
936936
},
937-
"GET /backend/timeout": {
938-
"environments": ["compute"],
939-
"downstream_response": {
940-
"status": 200,
941-
"body": "ok"
942-
}
943-
},
944-
"GET /implicit-dynamic-backend/dynamic-backends-disabled": {},
945-
"GET /implicit-dynamic-backend/dynamic-backends-enabled": {},
946-
"GET /implicit-dynamic-backend/dynamic-backends-enabled-called-twice": {},
947-
"GET /explicit-dynamic-backend/dynamic-backends-enabled-all-fields": {},
948-
"GET /explicit-dynamic-backend/dynamic-backends-enabled-minimal-fields": {},
949-
"GET /backend/interface": {},
950-
"GET /backend/constructor/called-as-regular-function": {},
951-
"GET /backend/constructor/empty-parameter": {},
952-
"GET /backend/constructor/parameter-not-an-object": {},
953-
"GET /backend/constructor/parameter-name-property-null": {},
954-
"GET /backend/constructor/parameter-name-property-undefined": {},
955-
"GET /backend/constructor/parameter-name-property-too-long": {},
956-
"GET /backend/constructor/parameter-name-property-empty-string": {},
957-
"GET /backend/constructor/parameter-name-property-calls-7.1.17-ToString": {},
958-
"GET /backend/constructor/parameter-target-property-null": {},
959-
"GET /backend/constructor/parameter-target-property-undefined": {},
960-
"GET /backend/constructor/parameter-target-property-empty-string": {},
961-
"GET /backend/constructor/parameter-target-property-calls-7.1.17-ToString": {},
962-
"GET /backend/constructor/parameter-target-property-valid-host": {},
963-
"GET /backend/constructor/parameter-target-property-invalid-host": {},
964-
"GET /backend/constructor/parameter-ciphers-property-empty-string": {},
965-
"GET /backend/constructor/parameter-ciphers-property-invalid-cipherlist-string": {},
966-
"GET /backend/constructor/parameter-ciphers-property-valid-cipherlist-strings-supported-by-fastly": {},
967-
"GET /backend/constructor/parameter-ciphers-property-valid-cipherlist-strings-but-not-supported-by-fastly": {},
968-
"GET /backend/constructor/parameter-ciphers-property-calls-7.1.17-ToString": {},
969-
"GET /backend/constructor/parameter-hostOverride-property-empty-string": {},
970-
"GET /backend/constructor/parameter-hostOverride-property-calls-7.1.17-ToString": {},
971-
"GET /backend/constructor/parameter-hostOverride-property-valid-string": {},
972-
"GET /backend/constructor/parameter-connectTimeout-property-negative-number": {},
973-
"GET /backend/constructor/parameter-connectTimeout-property-too-big": {},
974-
"GET /backend/constructor/parameter-connectTimeout-property-calls-7.1.4-ToNumber": {},
975-
"GET /backend/constructor/parameter-connectTimeout-property-valid-number": {},
976-
"GET /backend/constructor/parameter-firstByteTimeout-property-negative-number": {},
977-
"GET /backend/constructor/parameter-firstByteTimeout-property-too-big": {},
978-
"GET /backend/constructor/parameter-firstByteTimeout-property-calls-7.1.4-ToNumber": {},
979-
"GET /backend/constructor/parameter-firstByteTimeout-property-valid-number": {},
980-
"GET /backend/constructor/parameter-firstByteTimeout-property-invalid-number": {},
981-
"GET /backend/constructor/parameter-betweenBytesTimeout-property-negative-number": {},
982-
"GET /backend/constructor/parameter-betweenBytesTimeout-property-too-big": {},
983-
"GET /backend/constructor/parameter-betweenBytesTimeout-property-calls-7.1.4-ToNumber": {},
984-
"GET /backend/constructor/parameter-betweenBytesTimeout-property-valid-number": {},
985-
"GET /backend/constructor/parameter-useSSL-property-valid-boolean": {},
986-
"GET /backend/constructor/parameter-dontPool-property-valid-boolean": {},
987-
"GET /backend/constructor/parameter-tlsMinVersion-property-nan": {},
988-
"GET /backend/constructor/parameter-tlsMinVersion-property-invalid-number": {},
989-
"GET /backend/constructor/parameter-tlsMinVersion-property-calls-7.1.4-ToNumber": {},
990-
"GET /backend/constructor/parameter-tlsMinVersion-property-valid-number": {},
991-
"GET /backend/constructor/parameter-tlsMinVersion-greater-than-tlsMaxVersion": {},
992-
"GET /backend/constructor/parameter-tlsMaxVersion-property-nan": {},
993-
"GET /backend/constructor/parameter-tlsMaxVersion-property-invalid-number": {},
994-
"GET /backend/constructor/parameter-tlsMaxVersion-property-calls-7.1.4-ToNumber": {},
995-
"GET /backend/constructor/parameter-tlsMaxVersion-property-valid-number": {},
996-
"GET /backend/constructor/parameter-certificateHostname-property-empty-string": {},
997-
"GET /backend/constructor/parameter-certificateHostname-property-calls-7.1.17-ToString": {},
998-
"GET /backend/constructor/parameter-certificateHostname-property-valid-string": {},
999-
"GET /backend/constructor/parameter-caCertificate-property-empty-string": {},
1000-
"GET /backend/constructor/parameter-caCertificate-property-calls-7.1.17-ToString": {},
1001-
"GET /backend/constructor/parameter-caCertificate-property-valid-string": {},
1002-
"GET /backend/constructor/parameter-sniHostname-property-empty-string": {},
1003-
"GET /backend/constructor/parameter-sniHostname-property-calls-7.1.17-ToString": {},
1004-
"GET /backend/constructor/parameter-sniHostname-property-valid-string": {},
1005-
"GET /backend/constructor/parameter-clientCertificate-property-invalid": {},
1006-
"GET /backend/constructor/parameter-clientCertificate-certificate-property-missing": {},
1007-
"GET /backend/constructor/parameter-clientCertificate-certificate-property-invalid": {},
1008-
"GET /backend/constructor/parameter-clientCertificate-key-property-missing": {},
1009-
"GET /backend/constructor/parameter-clientCertificate-key-property-invalid": {},
1010-
"GET /backend/constructor/parameter-clientCertificate-key-property-fake": {},
1011-
"GET /backend/constructor/parameter-clientCertificate-valid": {},
1012-
"GET /backend/constructor/parameter-grpc-property-falsy": {},
1013-
"GET /backend/constructor/parameter-grpc-enabled": {},
1014-
"GET /backend/constructor/http-keepalive-invalid": {},
1015-
"GET /backend/constructor/http-keepalive": {},
1016-
"GET /backend/constructor/tcp-keepalive-invalid": {},
1017-
"GET /backend/constructor/tcp-keepalive": {},
1018-
"GET /backend/set-default-backend-configuration": {},
1019-
"GET /backend/health/called-as-constructor-function": {},
1020-
"GET /backend/health/empty-parameter": {},
1021-
"GET /backend/health/parameter-calls-7.1.17-ToString": {},
1022-
"GET /backend/health/parameter-invalid": {},
1023-
"GET /backend/health/happy-path-backend-exists": {},
1024-
"GET /backend/health/happy-path-backend-does-not-exist": {},
1025-
"GET /backend/port-ip-defined": {},
1026-
"GET /backend/port-ip-cached": {},
1027-
"GET /backend/props": {},
1028937
"GET /dictionary/exposed-as-global": {},
1029938
"GET /dictionary/interface": {},
1030939
"GET /dictionary/constructor/called-as-regular-function": {},
@@ -1080,90 +989,6 @@
1080989
"environments": ["compute"]
1081990
},
1082991
"GET /includeBytes": {},
1083-
"GET /kv-store/exposed-as-global": {},
1084-
"GET /kv-store/interface": {},
1085-
"GET /kv-store/constructor/called-as-regular-function": {},
1086-
"GET /kv-store/constructor/parameter-calls-7.1.17-ToString": {},
1087-
"GET /kv-store/constructor/empty-parameter": {},
1088-
"GET /kv-store/constructor/found-store": {},
1089-
"GET /kv-store/constructor/missing-store": {},
1090-
"GET /kv-store/constructor/invalid-name": {},
1091-
"GET /kv-store/put/called-as-constructor": {},
1092-
"GET /kv-store/put/called-unbound": {},
1093-
"GET /kv-store/put/key-parameter-calls-7.1.17-ToString": {},
1094-
"GET /kv-store/put/key-parameter-not-supplied": {},
1095-
"GET /kv-store/put/key-parameter-empty-string": {},
1096-
"GET /kv-store/put/key-parameter-1024-character-string": {},
1097-
"GET /kv-store/put/key-parameter-1025-character-string": {},
1098-
"GET /kv-store/put/key-parameter-containing-newline": {},
1099-
"GET /kv-store/put/key-parameter-containing-carriage-return": {},
1100-
"GET /kv-store/put/key-parameter-starting-with-well-known-acme-challenge": {},
1101-
"GET /kv-store/put/key-parameter-single-dot": {},
1102-
"GET /kv-store/put/key-parameter-double-dot": {},
1103-
"GET /kv-store/put/key-parameter-containing-special-characters": {},
1104-
"GET /kv-store/put/value-parameter-as-undefined": {},
1105-
"GET /kv-store/put/value-parameter-not-supplied": {},
1106-
"GET /kv-store/put/value-parameter-readablestream-empty": {},
1107-
"GET /kv-store/put/value-parameter-readablestream-under-30mb": {},
1108-
"GET /kv-store/put/value-parameter-readablestream-over-30mb": {},
1109-
"GET /kv-store/put/value-parameter-readablestream-locked": {},
1110-
"GET /kv-store/put/value-parameter-URLSearchParams": {},
1111-
"GET /kv-store/put/value-parameter-strings": {},
1112-
"GET /kv-store/put/value-parameter-string-over-30mb": {},
1113-
"GET /kv-store/put/value-parameter-calls-7.1.17-ToString": {},
1114-
"GET /kv-store/put/value-parameter-buffer": {},
1115-
"GET /kv-store/put/value-parameter-arraybuffer": {},
1116-
"GET /kv-store/put/value-parameter-typed-arrays": {},
1117-
"GET /kv-store/put/value-parameter-dataview": {},
1118-
"POST /kv-store/put/request-body": {
1119-
"environments": ["compute"],
1120-
"downstream_request": {
1121-
"method": "POST",
1122-
"pathname": "/kv-store/put/request-body",
1123-
"headers": ["Content-Type", "application/json"],
1124-
"body": "hello world!"
1125-
}
1126-
},
1127-
"GET /kv-store/delete/called-as-constructor": {},
1128-
"GET /kv-store/delete/called-unbound": {},
1129-
"GET /kv-store/delete/key-parameter-calls-7.1.17-ToString": {},
1130-
"GET /kv-store/delete/key-parameter-not-supplied": {},
1131-
"GET /kv-store/delete/key-parameter-empty-string": {},
1132-
"GET /kv-store/delete/key-parameter-1024-character-string": {},
1133-
"GET /kv-store/delete/key-parameter-1025-character-string": {},
1134-
"GET /kv-store/delete/key-parameter-containing-newline": {},
1135-
"GET /kv-store/delete/key-parameter-containing-carriage-return": {},
1136-
"GET /kv-store/delete/key-parameter-starting-with-well-known-acme-challenge": {},
1137-
"GET /kv-store/delete/key-parameter-single-dot": {},
1138-
"GET /kv-store/delete/key-parameter-double-dot": {},
1139-
"GET /kv-store/delete/key-parameter-containing-special-characters": {},
1140-
"GET /kv-store/delete/key-does-not-exist-returns-undefined": {},
1141-
"GET /kv-store/delete/key-exists": {},
1142-
"GET /kv-store/delete/delete-key-twice": {},
1143-
"GET /kv-store/delete/multiple-deletes-at-once": {},
1144-
"GET /kv-store/get/called-as-constructor": {},
1145-
"GET /kv-store/get/called-unbound": {},
1146-
"GET /kv-store/get/key-parameter-calls-7.1.17-ToString": {},
1147-
"GET /kv-store/get/key-parameter-not-supplied": {},
1148-
"GET /kv-store/get/key-parameter-empty-string": {},
1149-
"GET /kv-store/get/key-parameter-1024-character-string": {},
1150-
"GET /kv-store/get/key-parameter-1025-character-string": {},
1151-
"GET /kv-store/get/key-parameter-containing-newline": {},
1152-
"GET /kv-store/get/key-parameter-containing-carriage-return": {},
1153-
"GET /kv-store/get/key-parameter-starting-with-well-known-acme-challenge": {},
1154-
"GET /kv-store/get/key-parameter-single-dot": {},
1155-
"GET /kv-store/get/key-parameter-double-dot": {},
1156-
"GET /kv-store/get/key-parameter-containing-special-characters": {},
1157-
"GET /kv-store/get/key-does-not-exist-returns-null": {},
1158-
"GET /kv-store/get/key-exists": {},
1159-
"GET /kv-store/get/multiple-lookups-at-once": {},
1160-
"GET /kv-store-entry/interface": {},
1161-
"GET /kv-store-entry/text/valid": {},
1162-
"GET /kv-store-entry/json/valid": {},
1163-
"GET /kv-store-entry/json/invalid": {},
1164-
"GET /kv-store-entry/arrayBuffer/valid": {},
1165-
"GET /kv-store-entry/body": {},
1166-
"GET /kv-store-entry/bodyUsed": {},
1167992
"GET /logger": {
1168993
"environments": ["viceroy"],
1169994
"logs": ["ComputeLog :: Hello!"]

integration-tests/js-compute/fixtures/module-mode/fastly.toml.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ name = "js-test-app"
99
service_id = ""
1010

1111
[scripts]
12-
build = "node ../../../../js-compute-runtime-cli.js --enable-experimental-high-resolution-time-methods --enable-experimental-top-level-await src/index.js"
12+
build = "node ../../../../js-compute-runtime-cli.js --enable-experimental-high-resolution-time-methods --module-mode src/index.js"
1313

1414
[local_server]
1515

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env node
2+
3+
import { $ as zx } from 'zx';
4+
import { argv } from 'node:process';
5+
6+
const serviceName = argv[2];
7+
8+
const startTime = Date.now();
9+
10+
if (process.env.FASTLY_API_TOKEN === undefined) {
11+
zx.verbose = false;
12+
try {
13+
process.env.FASTLY_API_TOKEN = String(
14+
await zx`fastly profile token --quiet`,
15+
).trim();
16+
} catch {
17+
console.error(
18+
'No environment variable named FASTLY_API_TOKEN has been set and no default fastly profile exists.',
19+
);
20+
console.error(
21+
'In order to run the tests, either create a fastly profile using `fastly profile create` or export a fastly token under the name FASTLY_API_TOKEN',
22+
);
23+
process.exit(1);
24+
}
25+
zx.verbose = true;
26+
}
27+
28+
// Setup KV Stores
29+
{
30+
let stores = await (async function () {
31+
try {
32+
return JSON.parse(
33+
await zx`fastly kv-store list --quiet --json --token $FASTLY_API_TOKEN`,
34+
);
35+
} catch {
36+
return [];
37+
}
38+
})();
39+
40+
const existing = stores.Data.find(
41+
({ Name }) => Name === `example-test-kv-store`,
42+
);
43+
// For somereason the StarlingMonkey version of this contains "ID" instead of "StoreID"
44+
const STORE_ID = existing?.StoreID || existing?.ID;
45+
if (!STORE_ID) {
46+
process.env.STORE_ID = JSON.parse(
47+
await zx`fastly kv-store create --quiet --name='example-test-kv-store' --json --token $FASTLY_API_TOKEN`,
48+
).id;
49+
} else {
50+
process.env.STORE_ID = STORE_ID;
51+
}
52+
try {
53+
await zx`fastly resource-link create --service-name ${serviceName} --version latest --resource-id $STORE_ID --token $FASTLY_API_TOKEN --autoclone`;
54+
} catch (e) {
55+
if (!e.message.includes('Duplicate record')) throw e;
56+
}
57+
}
58+
59+
await zx`fastly service-version activate --service-name ${serviceName} --version latest --token $FASTLY_API_TOKEN`;
60+
61+
console.log(
62+
`Set up has finished! Took ${(Date.now() - startTime) / 1000} seconds to complete`,
63+
);

integration-tests/js-compute/fixtures/module-mode/src/assertions.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,13 @@ export function assertDoesNotThrow(func) {
134134
}
135135
}
136136

137-
export { deepEqual as deepStrictEqual };
137+
export function deepStrictEqual(a, b) {
138+
if (!deepEqual(a, b)) {
139+
throw new Error(
140+
`Expected ${a} to equal ${b}, got ${JSON.stringify(a, null, 2)}`,
141+
);
142+
}
143+
}
138144

139145
export function deepEqual(a, b) {
140146
var aKeys;
@@ -157,6 +163,15 @@ export function deepEqual(a, b) {
157163
if (b === null || typeB !== 'object') {
158164
return false;
159165
}
166+
if (Array.isArray(a) && Array.isArray(b)) {
167+
if (a.length !== b.length) return false;
168+
for (let i = 0; i < a.length; i++) {
169+
if (!deepEqual(a[i], b[i])) {
170+
return false;
171+
}
172+
}
173+
return true;
174+
}
160175
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {
161176
return false;
162177
}

0 commit comments

Comments
 (0)