Skip to content

Commit 33bb307

Browse files
committed
重构 setValue/valueUpdate 的数据传递,以应付大量并发访问情况
1 parent 04d6641 commit 33bb307

File tree

12 files changed

+499
-275
lines changed

12 files changed

+499
-275
lines changed

src/app/service/content/exec_script.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ export default class ExecScript {
6464
this.sandboxContext?.emitEvent(event, eventId, data);
6565
}
6666

67-
valueUpdate(data: ValueUpdateDataEncoded) {
68-
this.sandboxContext?.valueUpdate(data);
67+
valueUpdate(storageName: string, uuid: string, data: ValueUpdateDataEncoded[]) {
68+
this.sandboxContext?.valueUpdate(storageName, uuid, data);
6969
}
7070

7171
execContext: any;

src/app/service/content/gm_api.test.ts

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { compileScript, compileScriptCode } from "./utils";
66
import type { Message } from "@Packages/message/types";
77
import { encodeMessage } from "@App/pkg/utils/message_value";
88
import { v4 as uuidv4 } from "uuid";
9+
import { getStorageName } from "@App/pkg/utils/utils";
910
const nilFn: ScriptFunc = () => {};
1011

1112
const scriptRes = {
@@ -130,15 +131,16 @@ describe.concurrent("GM Api", () => {
130131
},
131132
})
132133
);
133-
exec.valueUpdate({
134-
id: actualCall.data.params[0],
135-
entries: encodeMessage([[actualCall.data.params[1], undefined, undefined]]),
136-
uuid: script.uuid,
137-
storageName: script.uuid,
138-
sender: { runFlag: exec.sandboxContext!.runFlag, tabId: -2 },
139-
valueUpdated: false,
140-
updatetime: Date.now(),
141-
});
134+
exec.valueUpdate(getStorageName(script), script.uuid, [
135+
{
136+
id: actualCall.data.params[0],
137+
entries: encodeMessage([[actualCall.data.params[1], undefined, undefined]]),
138+
uuid: script.uuid,
139+
storageName: getStorageName(script),
140+
sender: { runFlag: exec.sandboxContext!.runFlag, tabId: -2 },
141+
updatetime: Date.now(),
142+
},
143+
]);
142144
}
143145
};
144146
it.concurrent("GM_getValue", async () => {
@@ -194,7 +196,7 @@ describe.concurrent("GM Api", () => {
194196
const exec = new ExecScript(script, undefined, undefined, nilFn, envInfo);
195197
exec.scriptFunc = compileScript(compileScriptCode(script));
196198
const ret = await exec.exec();
197-
expect(ret).toEqual("test5-test2-test3-test1"); // TM也沒有sort
199+
expect(ret).toEqual("test5-test2-test3-test1"); // TM也没有sort
198200
});
199201

200202
it.concurrent("GM.listValues", async () => {
@@ -232,7 +234,7 @@ describe.concurrent("GM Api", () => {
232234
const retPromise = exec.exec();
233235
valueDaoUpdatetimeFix(mockSendMessage, exec, script);
234236
const ret = await retPromise;
235-
expect(ret).toEqual("test5-test2-test3-test1"); // TM也沒有sort
237+
expect(ret).toEqual("test5-test2-test3-test1"); // TM也没有sort
236238
});
237239

238240
it.concurrent("GM_getValues", async () => {
@@ -762,15 +764,16 @@ describe.concurrent("GM_value", () => {
762764
const retPromise = exec.exec();
763765
expect(mockSendMessage).toHaveBeenCalledTimes(1);
764766
// 模拟值变化
765-
exec.valueUpdate({
766-
id: "id-1",
767-
entries: encodeMessage([["param1", 123, undefined]]),
768-
uuid: script.uuid,
769-
storageName: script.uuid,
770-
sender: { runFlag: exec.sandboxContext!.runFlag, tabId: -2 },
771-
valueUpdated: true,
772-
updatetime: Date.now(),
773-
});
767+
exec.valueUpdate(getStorageName(script), script.uuid, [
768+
{
769+
id: "id-1",
770+
entries: encodeMessage([["param1", 123, undefined]]),
771+
uuid: script.uuid,
772+
storageName: getStorageName(script),
773+
sender: { runFlag: exec.sandboxContext!.runFlag, tabId: -2 },
774+
updatetime: Date.now(),
775+
},
776+
]);
774777
const ret = await retPromise;
775778
expect(ret).toEqual({ name: "param1", oldValue: undefined, newValue: 123, remote: false });
776779
});
@@ -798,15 +801,16 @@ describe.concurrent("GM_value", () => {
798801
const retPromise = exec.exec();
799802
expect(mockSendMessage).toHaveBeenCalledTimes(1);
800803
// 模拟值变化
801-
exec.valueUpdate({
802-
id: "id-2",
803-
entries: encodeMessage([["param2", 456, undefined]]),
804-
uuid: script.uuid,
805-
storageName: "testStorage",
806-
sender: { runFlag: "user", tabId: -2 },
807-
valueUpdated: true,
808-
updatetime: Date.now(),
809-
});
804+
exec.valueUpdate(getStorageName(script), script.uuid, [
805+
{
806+
id: "id-2",
807+
entries: encodeMessage([["param2", 456, undefined]]),
808+
uuid: script.uuid,
809+
storageName: "testStorage",
810+
sender: { runFlag: "user", tabId: -2 },
811+
updatetime: Date.now(),
812+
},
813+
]);
810814
const ret2 = await retPromise;
811815
expect(ret2).toEqual({ name: "param2", oldValue: undefined, newValue: 456, remote: true });
812816
});
@@ -834,15 +838,16 @@ describe.concurrent("GM_value", () => {
834838
expect(id).toBeTypeOf("string");
835839
expect(id.length).greaterThan(0);
836840
// 触发valueUpdate
837-
exec.valueUpdate({
838-
id: id,
839-
entries: encodeMessage([["a", 123, undefined]]),
840-
uuid: script.uuid,
841-
storageName: script.uuid,
842-
sender: { runFlag: exec.sandboxContext!.runFlag, tabId: -2 },
843-
valueUpdated: true,
844-
updatetime: Date.now(),
845-
});
841+
exec.valueUpdate(getStorageName(script), script.uuid, [
842+
{
843+
id: id,
844+
entries: encodeMessage([["a", 123, undefined]]),
845+
uuid: script.uuid,
846+
storageName: getStorageName(script),
847+
sender: { runFlag: exec.sandboxContext!.runFlag, tabId: -2 },
848+
updatetime: Date.now(),
849+
},
850+
]);
846851

847852
const ret = await retPromise;
848853
expect(ret).toEqual(123);

src/app/service/content/gm_api.ts

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { type TGMKeyValue } from "@App/app/repo/value";
2626
export interface IGM_Base {
2727
sendMessage(api: string, params: any[]): Promise<any>;
2828
connect(api: string, params: any[]): Promise<any>;
29-
valueUpdate(data: ValueUpdateDataEncoded): void;
29+
valueUpdate(storageName: string, uuid: string, data: ValueUpdateDataEncoded[]): void;
3030
emitEvent(event: string, eventId: string, data: any): void;
3131
}
3232

@@ -148,45 +148,52 @@ class GM_Base implements IGM_Base {
148148
}
149149

150150
@GMContext.protected()
151-
public valueUpdate(data: ValueUpdateDataEncoded) {
151+
public valueUpdate(storageName: string, uuid: string, list: ValueUpdateDataEncoded[]) {
152152
if (!this.scriptRes) return;
153153
const scriptRes = this.scriptRes;
154-
const { id, uuid, entries, storageName, sender, valueUpdated, updatetime } = data;
155-
if (uuid === scriptRes.uuid || storageName === getStorageName(scriptRes)) {
156-
const valueStore = scriptRes.value;
157-
const remote = sender.runFlag !== this.runFlag;
158-
if (!remote && id) {
159-
const fn = valueChangePromiseMap.get(id);
160-
if (fn) {
161-
valueChangePromiseMap.delete(id);
162-
fn();
154+
let lastUpdateTime = 0;
155+
if (uuid == scriptRes.uuid || storageName === getStorageName(scriptRes)) {
156+
for (const data of list) {
157+
const { id, entries, sender, updatetime } = data;
158+
const valueStore = scriptRes.value;
159+
const remote = sender.runFlag !== this.runFlag;
160+
if (!remote && id) {
161+
const fn = valueChangePromiseMap.get(id);
162+
if (fn) {
163+
valueChangePromiseMap.delete(id);
164+
fn();
165+
}
163166
}
164-
}
165-
if (valueUpdated) {
166-
const valueChanges = decodeMessage(entries);
167-
for (const [key, value, oldValue] of valueChanges) {
168-
// 触发,并更新值
169-
if (value === undefined) {
170-
if (valueStore[key] !== undefined) {
171-
delete valueStore[key];
167+
const isUpdated = entries.k.length > 0;
168+
if (isUpdated) {
169+
const valueChanges = decodeMessage(entries);
170+
for (const [key, value, oldValue] of valueChanges) {
171+
// 触发,并更新值
172+
if (value === undefined) {
173+
if (valueStore[key] !== undefined) {
174+
delete valueStore[key];
175+
}
176+
} else {
177+
valueStore[key] = value;
172178
}
173-
} else {
174-
valueStore[key] = value;
179+
this.valueChangeListener?.execute(key, oldValue, value, remote, sender.tabId);
175180
}
176-
this.valueChangeListener?.execute(key, oldValue, value, remote, sender.tabId);
181+
}
182+
if (updatetime) {
183+
lastUpdateTime = updatetime;
177184
}
178185
}
179-
if (updatetime) {
186+
if (lastUpdateTime) {
180187
const readFreshes = this.readFreshes;
181188
if (readFreshes) {
182189
for (const entry of readFreshes) {
183-
if (updatetime >= entry.updatetime) {
190+
if (lastUpdateTime >= entry.updatetime) {
184191
readFreshes.delete(entry);
185192
entry.resolveFn();
186193
}
187194
}
188195
}
189-
this.valueDaoUpdatetime = updatetime;
196+
this.valueDaoUpdatetime = lastUpdateTime;
190197
}
191198
}
192199
}

src/app/service/content/inject.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ExternalWhitelist } from "@App/app/const";
44
import { sendMessage } from "@Packages/message/client";
55
import type { ScriptExecutor } from "./script_executor";
66
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
7-
import type { GMInfoEnv, ValueUpdateDataEncoded } from "./types";
7+
import type { GMInfoEnv, ValueUpdateSendData } from "./types";
88

99
export class InjectRuntime {
1010
constructor(
@@ -20,7 +20,7 @@ export class InjectRuntime {
2020
// 转发给脚本
2121
this.scriptExecutor.emitEvent(data);
2222
});
23-
this.server.on("runtime/valueUpdate", (data: ValueUpdateDataEncoded) => {
23+
this.server.on("runtime/valueUpdate", (data: ValueUpdateSendData) => {
2424
this.scriptExecutor.valueUpdate(data);
2525
});
2626

src/app/service/content/script_executor.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Message } from "@Packages/message/types";
22
import { getStorageName } from "@App/pkg/utils/utils";
33
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
44
import ExecScript from "./exec_script";
5-
import type { GMInfoEnv, ScriptFunc, PreScriptFunc, ValueUpdateDataEncoded } from "./types";
5+
import type { GMInfoEnv, ScriptFunc, PreScriptFunc, ValueUpdateSendData } from "./types";
66
import { addStyle, definePropertyListener } from "./utils";
77

88
export type ExecScriptEntry = {
@@ -33,11 +33,13 @@ export class ScriptExecutor {
3333
}
3434
}
3535

36-
valueUpdate(data: ValueUpdateDataEncoded) {
37-
const { uuid, storageName } = data;
38-
for (const val of this.execMap.values()) {
39-
if (val.scriptRes.uuid === uuid || getStorageName(val.scriptRes) === storageName) {
40-
val.valueUpdate(data);
36+
valueUpdate(sendData: ValueUpdateSendData) {
37+
const { data, storageName } = sendData;
38+
for (const [uuid, list] of Object.entries(data)) {
39+
for (const val of this.execMap.values()) {
40+
if (val.scriptRes.uuid === uuid || getStorageName(val.scriptRes) === storageName) {
41+
val.valueUpdate(storageName, uuid, list);
42+
}
4143
}
4244
}
4345
}

src/app/service/content/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,14 @@ export type ValueUpdateDataEncoded = {
3131
uuid: string;
3232
storageName: string; // 储存name
3333
sender: ValueUpdateSender;
34-
valueUpdated: boolean;
3534
updatetime: number;
3635
};
3736

37+
export type ValueUpdateSendData = {
38+
storageName: string;
39+
data: Record<string, ValueUpdateDataEncoded[]>;
40+
};
41+
3842
// gm_api.ts
3943

4044
export interface ApiParam {

src/app/service/queue.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Script, SCRIPT_RUN_STATUS, SCRIPT_STATUS, SCRIPT_TYPE } from "../repo/scripts";
1+
import type { SCRIPT_RUN_STATUS, SCRIPT_STATUS, SCRIPT_TYPE } from "../repo/scripts";
22
import type {
33
InstallSource,
44
SWScriptMenuItemOption,
@@ -30,7 +30,7 @@ export type TEnableScript = { uuid: string; enable: boolean };
3030

3131
export type TScriptRunStatus = { uuid: string; runStatus: SCRIPT_RUN_STATUS };
3232

33-
export type TScriptValueUpdate = { script: Script; valueUpdated: boolean };
33+
export type TScriptValueUpdate = { uuid: string; valueUpdated: boolean; status: SCRIPT_STATUS; isEarlyStart: boolean };
3434

3535
export type TScriptMenuRegister = {
3636
uuid: string;

src/app/service/sandbox/runtime.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { CronJob } from "cron";
1313
import { proxyUpdateRunStatus } from "../offscreen/client";
1414
import { BgExecScriptWarp } from "../content/exec_warp";
1515
import type ExecScript from "../content/exec_script";
16-
import type { ValueUpdateData, ValueUpdateDataEncoded } from "../content/types";
16+
import type { ValueUpdateData, ValueUpdateSendData } from "../content/types";
1717
import { getStorageName, getMetadataStr, getUserConfigStr } from "@App/pkg/utils/utils";
1818
import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types";
1919
import { CATRetryError } from "../content/exec_warp";
@@ -323,19 +323,24 @@ export class Runtime {
323323
return this.execScript(loadScript, true);
324324
}
325325

326-
valueUpdate(data: ValueUpdateDataEncoded) {
327-
const dataNew = { ...data, entries: decodeMessage(data.entries) } as ValueUpdateData;
328-
// 转发给脚本
329-
this.execScripts.forEach((val) => {
330-
if (val.scriptRes.uuid === data.uuid || getStorageName(val.scriptRes) === data.storageName) {
331-
val.valueUpdate(data);
332-
}
333-
});
334-
// 更新crontabScripts中的脚本值
335-
for (const script of this.crontabSripts) {
336-
if (script.uuid === data.uuid || getStorageName(script) === data.storageName) {
337-
for (const [key, value, _oldValue] of dataNew.entries) {
338-
script.value[key] = value;
326+
valueUpdate(sendData: ValueUpdateSendData) {
327+
const storageName = sendData.storageName;
328+
for (const [uuid, list] of Object.entries(sendData.data)) {
329+
// 转发给脚本
330+
this.execScripts.forEach((val) => {
331+
if (val.scriptRes.uuid === uuid || getStorageName(val.scriptRes) === storageName) {
332+
val.valueUpdate(storageName, uuid, list);
333+
}
334+
});
335+
for (const data of list) {
336+
const dataNew = { ...data, entries: decodeMessage(data.entries) } as ValueUpdateData;
337+
// 更新crontabScripts中的脚本值
338+
for (const script of this.crontabSripts) {
339+
if (script.uuid === uuid || getStorageName(script) === storageName) {
340+
for (const [key, value, _oldValue] of dataNew.entries) {
341+
script.value[key] = value;
342+
}
343+
}
339344
}
340345
}
341346
}

src/app/service/service_worker/gm_api.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,10 +352,11 @@ export default class GMApi {
352352
throw new Error("param is failed");
353353
}
354354
const [id, key, value] = request.params as [string, string, any];
355-
await this.value.setValue(request.script.uuid, id, key, value, {
355+
const valueSender = {
356356
runFlag: request.runFlag,
357357
tabId: sender.getSender()?.tab?.id || -1,
358-
});
358+
};
359+
await this.value.setValues(request.script.uuid, id, { [key]: value }, valueSender, false);
359360
}
360361

361362
@PermissionVerify.API({ link: ["GM_deleteValues"] })

src/app/service/service_worker/runtime.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -437,15 +437,22 @@ export class RuntimeService {
437437
});
438438

439439
// 监听脚本值变更
440-
this.mq.subscribe<TScriptValueUpdate>("valueUpdate", async ({ script, valueUpdated }: TScriptValueUpdate) => {
441-
if (valueUpdated) {
442-
if (script.status === SCRIPT_STATUS_ENABLE && isEarlyStartScript(script.metadata)) {
443-
// 如果是预加载脚本,需要更新脚本代码重新注册
444-
// scriptMatchInfo 里的 value 改变 => compileInjectionCode -> injectionCode 改变
445-
await this.updateResourceOnScriptChange(script);
440+
this.mq.subscribe<TScriptValueUpdate>(
441+
"valueUpdate",
442+
async ({ uuid, valueUpdated, status, isEarlyStart }: TScriptValueUpdate) => {
443+
if (valueUpdated) {
444+
if (status === SCRIPT_STATUS_ENABLE && isEarlyStart) {
445+
// 如果是预加载脚本,需要更新脚本代码重新注册
446+
// scriptMatchInfo 里的 value 改变 => compileInjectionCode -> injectionCode 改变
447+
const script = await this.scriptDAO.get(uuid);
448+
// 因為從 scriptDAO 取了最新的。所以再確認一下吧。
449+
if (script && script.status === SCRIPT_STATUS_ENABLE && isEarlyStartScript(script.metadata)) {
450+
await this.updateResourceOnScriptChange(script);
451+
}
452+
}
446453
}
447454
}
448-
});
455+
);
449456

450457
if (chrome.extension.inIncognitoContext) {
451458
this.systemConfig.addListener("enable_script_incognito", async (enable) => {

0 commit comments

Comments
 (0)