egos-transfer 0.2.9 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,6 +31,12 @@ export declare abstract class PeerTransfer {
31
31
  private idleTimer;
32
32
  /** 心跳超时定时器:30 秒未收到 HEARTBEAT 则重连 */
33
33
  private heartbeatTimer;
34
+ /** 进行中的 Peer 创建:并发调用复用同一个 Promise,防止交叉 destroy/新建 */
35
+ private creatingPeer;
36
+ /** 上次成功创建 Peer 的时间戳:节流窗口内直接复用现有实例 */
37
+ private lastPeerCreatedAt;
38
+ /** 创建代际:stop/clearInstance/新一轮创建时递增,使旧的进行中创建作废 */
39
+ private createEpoch;
34
40
  constructor(deviceId: string, config: PeerTransferOptions);
35
41
  abstract getStore(): any;
36
42
  abstract online(deviceId: string): void;
@@ -43,6 +49,7 @@ export declare abstract class PeerTransfer {
43
49
  abstract onConnectLimit(): void;
44
50
  abstract reloadConfig(config?: Record<string, any>): Promise<void>;
45
51
  createPeer(force?: boolean): Promise<Peer>;
52
+ private doCreatePeer;
46
53
  private waitForOpen;
47
54
  handleSocketMessage(message: IMessage): void;
48
55
  connectToPeer(peerId: string): Promise<DataConnection | undefined>;
@@ -89,6 +96,8 @@ export declare abstract class PeerTransfer {
89
96
  * 与 destroy stop 后仍可调用 createPeer 恢复工作。
90
97
  */
91
98
  stop(): void;
99
+ /** 使进行中的 Peer 创建作废:旧创建在恢复点自行中止,并允许 createPeer 立即重启 */
100
+ private abortCreate;
92
101
  private destroyPeer;
93
102
  askReload(peerId: string): void;
94
103
  }
@@ -27,6 +27,10 @@ const IDLE_RECREATE_TIMEOUT = 30 * 60 * 1000;
27
27
  /** 30 秒未收到服务端 HEARTBEAT 则重连 */
28
28
  const HEARTBEAT_TIMEOUT = 30000;
29
29
  exports.PEER_CLOSE_RECONNECT_TIMEOUT = 3000;
30
+ /** Peer 创建节流窗口:窗口内禁止重复重建,防止事件风暴导致连续 create */
31
+ const PEER_RECREATE_THROTTLE = 5000;
32
+ /** 进行中的创建因 stop/clearInstance/新一轮创建而被中止时抛出的错误 */
33
+ const PEER_CREATION_ABORTED = 'peer creation aborted';
30
34
  class PeerTransfer {
31
35
  constructor(deviceId, config) {
32
36
  this.requests = new Map();
@@ -42,6 +46,12 @@ class PeerTransfer {
42
46
  this.idleTimer = null;
43
47
  /** 心跳超时定时器:30 秒未收到 HEARTBEAT 则重连 */
44
48
  this.heartbeatTimer = null;
49
+ /** 进行中的 Peer 创建:并发调用复用同一个 Promise,防止交叉 destroy/新建 */
50
+ this.creatingPeer = null;
51
+ /** 上次成功创建 Peer 的时间戳:节流窗口内直接复用现有实例 */
52
+ this.lastPeerCreatedAt = 0;
53
+ /** 创建代际:stop/clearInstance/新一轮创建时递增,使旧的进行中创建作废 */
54
+ this.createEpoch = 0;
45
55
  this.peerConfig = Object.assign(Object.assign({}, config), { key: String(Date.now()) });
46
56
  this.peerId = deviceId;
47
57
  this.deviceId = deviceId;
@@ -57,15 +67,50 @@ class PeerTransfer {
57
67
  return __awaiter(this, void 0, void 0, function* () {
58
68
  var _a;
59
69
  this._exited = false;
70
+ // 并发去重:同一时刻只允许一次创建,后续调用复用进行中的 Promise
71
+ if (this.creatingPeer) {
72
+ return this.creatingPeer;
73
+ }
60
74
  if (((_a = this.peer) === null || _a === void 0 ? void 0 : _a.open) && !force) {
61
75
  return this.peer;
62
76
  }
77
+ // 节流:距离上次创建尝试不足窗口期时直接返回现有实例,防止失败后热循环重建(force 可穿透)
78
+ if (!force && Date.now() - this.lastPeerCreatedAt < PEER_RECREATE_THROTTLE) {
79
+ return this.peer;
80
+ }
63
81
  console.info('create peer', Date.now());
82
+ const createPromise = this.doCreatePeer();
83
+ this.creatingPeer = createPromise;
84
+ try {
85
+ return yield createPromise;
86
+ }
87
+ finally {
88
+ // 仅当未被新一轮创建/abort 覆盖时才清理,避免误清新创建的引用
89
+ if (this.creatingPeer === createPromise) {
90
+ this.creatingPeer = null;
91
+ }
92
+ }
93
+ });
94
+ }
95
+ doCreatePeer() {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ const epoch = ++this.createEpoch; // 本轮创建的代际号
98
+ const isStale = () => epoch !== this.createEpoch;
99
+ this.lastPeerCreatedAt = Date.now(); // 记录创建尝试时间,节流窗口从尝试时刻起算
64
100
  this.destroyPeer();
65
101
  yield this.reloadConfig();
102
+ // reloadConfig 期间可能发生 stop()/destroy(),旧创建作废
103
+ if (isStale()) {
104
+ throw new Error(PEER_CREATION_ABORTED);
105
+ }
66
106
  const peer = new peerjs_1.Peer(this.peerId, Object.assign(Object.assign({}, this.peerConfig), { pingInterval: 9000 }));
67
107
  this.peer = peer;
68
108
  yield this.waitForOpen(peer);
109
+ // waitForOpen 期间可能发生 stop()/destroy(),旧创建作废
110
+ if (isStale()) {
111
+ peer.destroy();
112
+ throw new Error(PEER_CREATION_ABORTED);
113
+ }
69
114
  this.destroyed = false;
70
115
  peer.on('close', () => {
71
116
  // 仅当 Peer 非主动销毁(stop/clearInstance/重建)时才重连
@@ -467,7 +512,7 @@ class PeerTransfer {
467
512
  onRequest(conn, message) {
468
513
  return __awaiter(this, void 0, void 0, function* () {
469
514
  console.info('onRequest===>', message.route);
470
- this.resetIdleTimer();
515
+ // this.resetIdleTimer();
471
516
  try {
472
517
  yield this.router.run(conn, message);
473
518
  }
@@ -479,7 +524,7 @@ class PeerTransfer {
479
524
  onResponse(_conn, message) {
480
525
  return __awaiter(this, void 0, void 0, function* () {
481
526
  console.info('onResponse<====', message.route);
482
- this.resetIdleTimer();
527
+ // this.resetIdleTimer();
483
528
  // 优先检查 health check ping 回调
484
529
  const cb = this.requests.get(message.requestId);
485
530
  if (cb) {
@@ -539,6 +584,7 @@ class PeerTransfer {
539
584
  }
540
585
  clearInstance() {
541
586
  this.destroyed = true;
587
+ this.abortCreate();
542
588
  this.clearIdleTimer();
543
589
  this.clearHeartbeatTimer();
544
590
  this.connectionManager.destroy();
@@ -556,6 +602,7 @@ class PeerTransfer {
556
602
  */
557
603
  stop() {
558
604
  this._exited = true;
605
+ this.abortCreate();
559
606
  this.clearIdleTimer();
560
607
  this.clearHeartbeatTimer();
561
608
  this.connectionManager.stopHealthCheck();
@@ -565,6 +612,12 @@ class PeerTransfer {
565
612
  this.destroyPeer();
566
613
  this.reconnectAttempt = 0;
567
614
  }
615
+ /** 使进行中的 Peer 创建作废:旧创建在恢复点自行中止,并允许 createPeer 立即重启 */
616
+ abortCreate() {
617
+ this.createEpoch++; // 旧创建的 isStale() 变为 true,自行 destroy 并抛错
618
+ this.creatingPeer = null;
619
+ this.lastPeerCreatedAt = 0; // 重置节流,createPeer 可立即再次创建
620
+ }
568
621
  destroyPeer() {
569
622
  var _a, _b;
570
623
  (_a = this.peer) === null || _a === void 0 ? void 0 : _a.destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "egos-transfer",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "1234",
5
5
  "homepage": "https://github.com/superbogy/peer-transfer#readme",
6
6
  "bugs": {
@@ -41,6 +41,10 @@ const IDLE_RECREATE_TIMEOUT = 30 * 60 * 1000;
41
41
  const HEARTBEAT_TIMEOUT = 30000;
42
42
 
43
43
  export const PEER_CLOSE_RECONNECT_TIMEOUT = 3000;
44
+ /** Peer 创建节流窗口:窗口内禁止重复重建,防止事件风暴导致连续 create */
45
+ const PEER_RECREATE_THROTTLE = 5000;
46
+ /** 进行中的创建因 stop/clearInstance/新一轮创建而被中止时抛出的错误 */
47
+ const PEER_CREATION_ABORTED = 'peer creation aborted';
44
48
 
45
49
  export abstract class PeerTransfer {
46
50
  protected peer: Peer | undefined;
@@ -62,6 +66,12 @@ export abstract class PeerTransfer {
62
66
  private idleTimer: ReturnType<typeof setTimeout> | null = null;
63
67
  /** 心跳超时定时器:30 秒未收到 HEARTBEAT 则重连 */
64
68
  private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;
69
+ /** 进行中的 Peer 创建:并发调用复用同一个 Promise,防止交叉 destroy/新建 */
70
+ private creatingPeer: Promise<Peer> | null = null;
71
+ /** 上次成功创建 Peer 的时间戳:节流窗口内直接复用现有实例 */
72
+ private lastPeerCreatedAt = 0;
73
+ /** 创建代际:stop/clearInstance/新一轮创建时递增,使旧的进行中创建作废 */
74
+ private createEpoch = 0;
65
75
 
66
76
  constructor(deviceId: string, config: PeerTransferOptions) {
67
77
  this.peerConfig = { ...config, key: String(Date.now()) };
@@ -90,17 +100,51 @@ export abstract class PeerTransfer {
90
100
 
91
101
  async createPeer(force?: boolean): Promise<Peer> {
92
102
  this._exited = false;
103
+ // 并发去重:同一时刻只允许一次创建,后续调用复用进行中的 Promise
104
+ if (this.creatingPeer) {
105
+ return this.creatingPeer;
106
+ }
93
107
  if (this.peer?.open && !force) {
94
108
  return this.peer;
95
109
  }
110
+ // 节流:距离上次创建尝试不足窗口期时直接返回现有实例,防止失败后热循环重建(force 可穿透)
111
+ if (!force && Date.now() - this.lastPeerCreatedAt < PEER_RECREATE_THROTTLE) {
112
+ return this.peer!;
113
+ }
96
114
  console.info('create peer', Date.now());
115
+ const createPromise = this.doCreatePeer();
116
+ this.creatingPeer = createPromise;
117
+ try {
118
+ return await createPromise;
119
+ } finally {
120
+ // 仅当未被新一轮创建/abort 覆盖时才清理,避免误清新创建的引用
121
+ if (this.creatingPeer === createPromise) {
122
+ this.creatingPeer = null;
123
+ }
124
+ }
125
+ }
126
+
127
+ private async doCreatePeer(): Promise<Peer> {
128
+ const epoch = ++this.createEpoch; // 本轮创建的代际号
129
+ const isStale = () => epoch !== this.createEpoch;
130
+ this.lastPeerCreatedAt = Date.now(); // 记录创建尝试时间,节流窗口从尝试时刻起算
97
131
  this.destroyPeer();
98
132
  await this.reloadConfig();
99
133
 
134
+ // reloadConfig 期间可能发生 stop()/destroy(),旧创建作废
135
+ if (isStale()) {
136
+ throw new Error(PEER_CREATION_ABORTED);
137
+ }
138
+
100
139
  const peer = new Peer(this.peerId, { ...this.peerConfig, pingInterval: 9000 });
101
140
  this.peer = peer;
102
141
 
103
142
  await this.waitForOpen(peer);
143
+ // waitForOpen 期间可能发生 stop()/destroy(),旧创建作废
144
+ if (isStale()) {
145
+ peer.destroy();
146
+ throw new Error(PEER_CREATION_ABORTED);
147
+ }
104
148
  this.destroyed = false;
105
149
  peer.on('close', () => {
106
150
  // 仅当 Peer 非主动销毁(stop/clearInstance/重建)时才重连
@@ -562,7 +606,7 @@ export abstract class PeerTransfer {
562
606
 
563
607
  async onRequest(conn: DataConnection, message: PeerMessage) {
564
608
  console.info('onRequest===>', message.route);
565
- this.resetIdleTimer();
609
+ // this.resetIdleTimer();
566
610
  try {
567
611
  await this.router.run(conn, message);
568
612
  } catch (err: any) {
@@ -572,7 +616,7 @@ export abstract class PeerTransfer {
572
616
 
573
617
  async onResponse(_conn: DataConnection, message: PeerMessage) {
574
618
  console.info('onResponse<====', message.route);
575
- this.resetIdleTimer();
619
+ // this.resetIdleTimer();
576
620
  // 优先检查 health check ping 回调
577
621
  const cb = this.requests.get(message.requestId);
578
622
  if (cb) {
@@ -641,6 +685,7 @@ export abstract class PeerTransfer {
641
685
 
642
686
  clearInstance() {
643
687
  this.destroyed = true;
688
+ this.abortCreate();
644
689
  this.clearIdleTimer();
645
690
  this.clearHeartbeatTimer();
646
691
 
@@ -662,6 +707,7 @@ export abstract class PeerTransfer {
662
707
  */
663
708
  stop() {
664
709
  this._exited = true;
710
+ this.abortCreate();
665
711
  this.clearIdleTimer();
666
712
  this.clearHeartbeatTimer();
667
713
  this.connectionManager.stopHealthCheck();
@@ -672,6 +718,13 @@ export abstract class PeerTransfer {
672
718
  this.reconnectAttempt = 0;
673
719
  }
674
720
 
721
+ /** 使进行中的 Peer 创建作废:旧创建在恢复点自行中止,并允许 createPeer 立即重启 */
722
+ private abortCreate() {
723
+ this.createEpoch++; // 旧创建的 isStale() 变为 true,自行 destroy 并抛错
724
+ this.creatingPeer = null;
725
+ this.lastPeerCreatedAt = 0; // 重置节流,createPeer 可立即再次创建
726
+ }
727
+
675
728
  private destroyPeer() {
676
729
  this.peer?.destroy();
677
730
  this.peer?.removeAllListeners();