-
Notifications
You must be signed in to change notification settings - Fork 403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: send data by chunk in websocket #3988
Conversation
Walkthrough该 PR 修改涉及多个模块。 Changes
Sequence Diagram(s)sequenceDiagram
participant 客户端 as Client
participant WSConn as WSWebSocketConnection
participant Socket as WebSocket
客户端->>WSConn: send(data)
WSConn->>WSConn: 将 data 入队 (sendQueue)
WSConn->>WSConn: 调用 processSendQueue() 处理队列
WSConn->>Socket: 异步分片发送数据
Socket-->>WSConn: 发送确认或错误反馈
WSConn->>WSConn: 更新 pendingSize 及异常处理
WSConn->>客户端: 返回 Promise resolve/reject
sequenceDiagram
participant Socket as 数据流
participant Decoder as LengthFieldBasedFrameDecoder
participant 日志 as Logger
Socket->>Decoder: push(data)
Decoder->>Decoder: 异步执行 readFrame(),读取数据帧
alt 解析成功
Decoder-->>Socket: 返回完整数据帧
else 解析失败
Decoder->>日志: 记录错误信息
end
Suggested reviewers
Warning There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure. 🔧 ESLint
yarn install v1.22.22 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (9)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
🚅 Previously deployed to Railway in the core project. Environment has been deleted. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Outside diff range, codebase verification and nitpick comments (7)
packages/connection/src/common/constants.ts (1)
3-6
: 代码变更看起来不错!新增的
chunkSize
常量与 PR 的目标一致,用于实现 WebSocket 连接的分块数据传输。8MB 的大小选择合理。建议为注释添加英文翻译,以便于国际协作。例如:
/** * 分片大小, 8MB + * Chunk size, 8MB */ export const chunkSize = 8 * 1024 * 1024;
packages/connection/src/common/connection/drivers/ws-websocket.ts (1)
11-11
: 新增的decoder
成员看起来不错,但可以考虑添加注释。新增的
decoder
成员用于处理传入的消息,这是一个很好的改进。建议为
decoder
成员添加简短的注释,解释其用途和重要性。例如:// 用于解码和处理传入的 WebSocket 消息 protected decoder = new LengthFieldBasedFrameDecoder();packages/connection/src/common/connection/drivers/reconnecting-websocket.ts (2)
20-30
: 更新构造函数和 send 方法构造函数中新增的事件监听器和
send
方法的重写都很好地实现了分块数据传输的目标。这些更改与 PR 的目标一致,有助于提高大文件传输的性能。然而,我建议在
send
方法中添加一个注释,解释为什么要使用分块传输,以及chunkSize
的值是多少。这将有助于其他开发者理解这个实现的目的。建议在
send
方法开始处添加如下注释:/** * 发送数据,使用分块传输以避免大文件传输时的线程阻塞。 * 每个块的大小为 ${chunkSize} 字节。 */
87-103
: 新增 dataHandler 方法和更新 dispose 方法新增的
dataHandler
方法很好地处理了不同类型的传入数据,包括 Blob、ArrayBuffer 和 Buffer。这种实现提高了代码的健壮性。dispose
方法的更新确保了正确的资源清理。然而,我建议在
dataHandler
方法中添加错误处理,以防在数据处理过程中出现异常。建议在
dataHandler
方法中添加错误处理:private dataHandler = (e: MessageEvent) => { // ... 现有代码 ... buffer.then((v) => this.decoder.push(new Uint8Array(v, 0, v.byteLength))) .catch((error) => { console.error('处理传入消息时出错:', error); // 可以在这里添加额外的错误处理逻辑 }); };packages/connection/src/common/connection/drivers/frame-decoder.ts (2)
67-69
: 数据发送逻辑的更新这个更改与新的监听器管理方法保持一致,直接调用
_onDataListener
而不是发出事件。建议添加一个空值检查,以增加代码的健壮性:
if (this._onDataListener && typeof this._onDataListener === 'function') { this._onDataListener(binary); }这样可以避免在
_onDataListener
不是函数时可能出现的运行时错误。
166-166
: dispose 方法的更新这个更改与新的监听器管理方法保持一致,但是使用
undefined
而不是null
与onData
方法中的做法不一致。为了保持一致性,建议将这行改为:
this._onDataListener = null;这样可以确保在整个类中使用相同的方式来表示监听器被移除。
packages/connection/__test__/common/frame-decoder.test.ts (1)
Line range hint
38-121
: 总结:数据包构造方法的系统性更新整个文件中,所有数据包构造的地方都一致地添加了
.dump()
方法的调用。这种系统性的更改可能反映了LengthFieldBasedFrameDecoder
类实现的变化。建议:
- 确保这些更改与
LengthFieldBasedFrameDecoder
类的最新实现保持同步。- 考虑在测试文件的开头添加一个注释,解释为什么需要使用
.dump()
方法,以及它对测试结果的影响。- 如果
.dump()
方法改变了数据包的结构或内容,可能需要更新其他依赖于这些测试的部分。
packages/connection/src/common/connection/drivers/ws-websocket.ts
Outdated
Show resolved
Hide resolved
9d104a8
to
a8ae167
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (2)
packages/connection/src/common/connection/drivers/ws-websocket.ts (1)
27-29
:⚠️ Potential issue建议在消息处理添加错误处理机制
在构造函数中,当调用
this.decoder.push(data)
时,建议添加错误处理,以防止在数据解码过程中出现未捕获的异常导致程序崩溃。您可以考虑如下修改:
this.socket.on('message', (data: Buffer) => { try { this.decoder.push(data); } catch (error) { console.error('Error decoding message:', error); // 根据需要添加进一步的错误处理逻辑 } });packages/connection/src/common/connection/drivers/frame-decoder.ts (1)
27-38
: 🛠️ Refactor suggestion将多监听器改为单一监听器可能限制功能
当前的修改将事件监听机制从支持多个监听器更改为仅支持单个监听器,这可能限制了代码的可扩展性,影响到需要添加多个监听器的需求。
建议考虑继续支持多个监听器,您可以使用数组来保存监听器列表,并在接收到数据时遍历调用。
例如:
private _onDataListeners: Array<(data: Uint8Array) => void> = []; onData(listener: (data: Uint8Array) => void) { this._onDataListeners.push(listener); return { dispose: () => { const index = this._onDataListeners.indexOf(listener); if (index > -1) { this._onDataListeners.splice(index, 1); } }, }; } protected async processBuffers(): Promise<void> { // ... existing code ... if (this._onDataListeners.length > 0) { for (const listener of this._onDataListeners) { try { await Promise.resolve().then(() => listener(binary)); } catch (error) { console.error('[Frame Decoder] Error in data listener:', error); } } } }
🧹 Nitpick comments (6)
packages/connection/src/common/connection/drivers/ws-websocket.ts (1)
19-19
: 建议将MAX_QUEUE_SIZE
设为可配置项当前
MAX_QUEUE_SIZE
被设置为固定值100,建议将其设为可配置参数,以便根据实际需求调整队列大小,增强系统的灵活性。packages/components/src/recycle-tree/tree/TreeNode.ts (1)
1388-1434
: 优化了hardReloadChildren
方法,提升了大数据量时的性能通过多项优化措施,如预先分配数组大小、采用动态批处理、缓存频繁访问的属性和方法、使用
for
循环替代for...of
,以及合并监听器更新等,显著提高了处理大量节点时的性能。然而,
hardReloadChildren
方法较长且复杂,建议将部分逻辑提取到辅助函数中,以提高代码的可读性和可维护性。packages/connection/__test__/browser/index.test.ts (1)
28-34
: 建议优化错误处理在发送响应消息时,建议添加错误处理逻辑。
建议修改如下:
- connection.send( + connection.send( furySerializer.serialize({ id: msgObj.id, kind: 'server-ready', traceId: '', }), - ); + ).catch((error) => { + console.error('Failed to send server-ready message:', error); + });packages/connection/__test__/common/frame-decoder.test.ts (2)
38-40
: 测试数据构造方法改进使用
dump()
方法处理数据包的实现很好,但建议添加对异常情况的测试。建议添加以下测试场景:
- 处理超大数据包时的内存使用情况
- 数据包损坏的情况
- 网络延迟模拟
52-57
: 建议优化测试用例结构当前的混合数据包测试实现较好,但可以进一步提升可维护性。
建议将测试数据的创建逻辑抽取为独立的辅助函数:
+function createMixedPacket(payload: Uint8Array, headerSize: number = 1024) { + const sumiPacket = LengthFieldBasedFrameDecoder.construct(payload).dump(); + const newPacket = createPayload(headerSize + sumiPacket.byteLength); + newPacket.set(sumiPacket, headerSize); + return [newPacket, payload] as const; +} + const mixedPackets = [p1m, p5m].map((v) => { - const sumiPacket = LengthFieldBasedFrameDecoder.construct(v).dump(); - const newPacket = createPayload(1024 + sumiPacket.byteLength); - newPacket.set(sumiPacket, 1024); - return [newPacket, v] as const; + return createMixedPacket(v); });packages/connection/src/common/buffers/buffers.ts (1)
76-107
: 建议重构以减少代码重复
slice4
方法与slice
方法有大量重复代码。建议重构以提高可维护性。建议使用以下实现:
slice4(start: number) { - let end = start + 4; - const buffers = this.buffers; - - if (end > this.size) { - end = this.size; - } - - if (start >= end) { - return emptyBuffer; - } - - let startBytes = 0; - let si = 0; - for (; si < buffers.length && startBytes + buffers[si].length <= start; si++) { - startBytes += buffers[si].length; - } - - const target = buffer4Capacity; - - let ti = 0; - for (let ii = si; ti < end - start && ii < buffers.length; ii++) { - const len = buffers[ii].length; - - const _start = ti === 0 ? start - startBytes : 0; - const _end = ti + len >= end - start ? Math.min(_start + (end - start) - ti, len) : len; - copy(buffers[ii], target, ti, _start, _end); - ti += _end - _start; - } - - return target; + const end = start + 4; + if (start >= end || start >= this.size) { + return emptyBuffer; + } + const slice = this.slice(start, end); + if (slice === emptyBuffer) { + return emptyBuffer; + } + copy(slice, buffer4Capacity, 0, 0, slice.length); + return buffer4Capacity; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
packages/components/src/recycle-tree/tree/TreeNode.ts
(3 hunks)packages/connection/__test__/browser/index.test.ts
(2 hunks)packages/connection/__test__/common/frame-decoder.test.ts
(4 hunks)packages/connection/src/common/buffers/buffers.ts
(3 hunks)packages/connection/src/common/connection/drivers/frame-decoder.ts
(5 hunks)packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
(3 hunks)packages/connection/src/common/connection/drivers/stream.ts
(1 hunks)packages/connection/src/common/connection/drivers/ws-websocket.ts
(2 hunks)packages/connection/src/common/constants.ts
(1 hunks)packages/connection/src/node/common-channel-handler.ts
(1 hunks)packages/core-browser/__tests__/bootstrap/connection.test.ts
(2 hunks)
🔇 Additional comments (9)
packages/connection/src/common/connection/drivers/ws-websocket.ts (1)
114-121
: 在 dispose 方法中拒绝所有待发送消息是一个很好的实践通过在连接关闭时拒绝所有待发送的消息,可以防止出现未处理的 Promise,保持应用程序状态的一致性。
packages/components/src/recycle-tree/tree/TreeNode.ts (2)
42-50
: 优化了spliceArray
函数,提高了性能新的实现通过在没有修改操作时直接返回原数组,并使用
slice
和concat
方法替代展开运算符,减少了不必要的操作,提高了函数的性能。
1381-1381
: 在异常处理中添加了return false
;在
catch
块中添加了return false;
,确保在发生异常时方法返回正确的状态,符合方法的返回类型,提高了代码的健壮性。packages/connection/src/common/constants.ts (1)
3-6
: 新增了chunkSize
常量添加了用于数据传输分片处理的常量
chunkSize
,大小为 1 * 1024 * 1024(1MB),为后续的 WebSocket 数据分片发送提供了基础。packages/core-browser/__tests__/bootstrap/connection.test.ts (1)
3-3
: 使用sleep
函数优化了延迟处理引入并使用了
sleep
函数,替代了原先基于setTimeout
的 Promise 实现,简化了代码,提高了可读性和可维护性。Also applies to: 38-40
packages/connection/src/node/common-channel-handler.ts (1)
45-45
: 代码优化得当!直接在
receiveConnection
方法中创建WSWebSocketConnection
实例,避免了不必要的中间变量,使代码更简洁。packages/connection/__test__/browser/index.test.ts (1)
24-25
: 测试用例实现合理!使用
WSWebSocketConnection
替代直接的 WebSocket 操作,更好地测试了新的分块传输功能。packages/connection/src/common/buffers/buffers.ts (2)
305-309
: 实现正确且符合设计模式
read4
方法的实现简洁明了,并且与现有的read
方法保持一致的设计模式。
8-8
: 注意:重用缓冲区可能导致并发问题
buffer4Capacity
作为静态缓冲区被重用可能在并发场景下导致数据竞争。建议添加相关文档说明此限制。建议添加以下注释说明:
+/** + * 警告:此缓冲区在多个调用之间共享和重用。 + * 在并发场景下使用时需要确保正确的同步。 + */ export const buffer4Capacity = new Uint8Array(4);✅ Verification successful
建议为 buffer4Capacity 添加警告注释
在文件
packages/connection/src/common/buffers/buffers.ts
的第 8 行中,声明的静态缓冲区buffer4Capacity
被设计为重复使用以避免重复分配。但需要注意的是,在并发使用场景下,该缓冲区的重用可能会引发数据竞争问题。建议在该常量前添加具体的警告注释,说明该缓冲区在并发环境下需要额外注意并做好同步保护,例如:+/** + * 警告:此缓冲区在多个调用之间共享和重用。 + * 在并发场景下使用时需要确保正确的同步,以避免数据竞争。 + */ export const buffer4Capacity = new Uint8Array(4);这样能更好地提醒开发者注意潜在的并发问题。
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
Outdated
Show resolved
Hide resolved
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
Outdated
Show resolved
Hide resolved
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
Show resolved
Hide resolved
packages/connection/src/common/connection/drivers/frame-decoder.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts (2)
30-58
: 建议优化分块发送逻辑当前实现中,每个分块都创建新的 Promise,这可能导致不必要的开销。考虑以下优化建议:
- 使用批量处理减少 Promise 创建次数
- 添加可配置的延迟以控制发送速率
建议重构为:
private async processSendQueue() { if (this.sending) { return; } this.sending = true; while (this.sendQueue.length > 0) { const { data, resolve, reject } = this.sendQueue[0]; try { const handle = LengthFieldBasedFrameDecoder.construct(data).dumpAndOwn(); const packet = handle.get(); + const chunks = []; for (let i = 0; i < packet.byteLength; i += chunkSize) { - await new Promise<void>((resolve) => { - const chunk = packet.subarray(i, Math.min(i + chunkSize, packet.byteLength)); - this.socket.send(chunk); - resolve(); - }); + chunks.push(packet.subarray(i, Math.min(i + chunkSize, packet.byteLength))); } + + for (const chunk of chunks) { + this.socket.send(chunk); + await new Promise(resolve => setTimeout(resolve, 0)); // 控制发送速率 + } handle.dispose(); resolve(); } catch (error) {
60-65
: 建议添加方法文档
send
方法的实现看起来不错,但建议添加 JSDoc 文档说明以下内容:
- 方法的异步特性
- 可能的错误类型
- 分块发送的大小限制
建议添加如下文档:
+/** + * 异步发送数据,支持大文件分块传输 + * @param data 要发送的数据 + * @returns Promise 在数据完全发送后 resolve,发送出错时 reject + * @throws Error 当 WebSocket 连接关闭或发送失败时 + */ send(data: Uint8Array): Promise<void> {packages/connection/src/common/connection/drivers/frame-decoder.ts (2)
92-126
: 建议优化帧处理性能当前实现中的异步处理可能导致不必要的延迟。建议:
- 优化内存使用,避免频繁的数据拷贝
- 考虑使用
TypedArray
的视图而不是切片建议添加性能监控:
protected async readFrame(): Promise<boolean> { + const startTime = performance.now(); try { const found = this.readLengthField(); if (!found) { return true; } const start = this.cursor.offset; const end = start + this.contentLength; if (end > this.buffers.byteLength) { return true; } const binary = this.buffers.slice(start, end); // 立即清理已处理的数据 this.buffers.splice(0, end); this.reset(); if (this._onDataListener) { try { await Promise.resolve().then(() => this._onDataListener?.(binary)); } catch (error) { console.error('[Frame Decoder] Error in data listener:', error); } } + const duration = performance.now() - startTime; + if (duration > 100) { // 记录处理时间超过 100ms 的帧 + console.warn('[Frame Decoder] Slow frame processing:', { + size: binary.byteLength, + duration + }); + } return false; } catch (error) {
212-225
: 建议完善静态方法文档
construct
静态方法的实现看起来不错,但建议添加更详细的文档说明其用途和注意事项。建议添加如下文档:
+/** + * 构造一个新的帧 + * @param content 要编码的内容 + * @returns 返回一个新的 writer 实例,调用者负责调用 dispose 释放资源 + * @throws Error 当编码失败时抛出错误 + * @example + * const writer = LengthFieldBasedFrameDecoder.construct(data); + * try { + * const frame = writer.dumpAndOwn(); + * // 使用 frame + * } finally { + * writer.dispose(); + * } + */ static construct(content: Uint8Array) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/connection/src/common/connection/drivers/frame-decoder.ts
(5 hunks)packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
(3 hunks)packages/connection/src/common/connection/drivers/stream.ts
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/connection/src/common/connection/drivers/stream.ts
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: build-windows
- GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (3)
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts (1)
20-28
: 构造函数可见性变更提醒将构造函数改为
protected
限制了类的实例化方式,现在只能通过static forURL
方法创建实例。这增强了封装性,但可能影响现有代码的扩展性。请确认此更改不会影响现有的继承类。
packages/connection/src/common/connection/drivers/frame-decoder.ts (2)
27-38
: 监听器实现限制提醒当前实现将多监听器模式改为单一监听器模式,这可能在某些场景下造成限制。建议考虑以下几点:
- 是否有需要支持多个监听器的场景
- 是否需要提供移除监听器的方法
请确认这个更改不会影响现有的使用场景。
54-69
: 🛠️ Refactor suggestion建议改进大数据包处理策略
当前实现在收到超大数据包时直接截断,这可能导致数据不完整。建议:
- 考虑拒绝而不是截断超大数据包
- 添加数据包大小的日志记录
建议修改为:
push(chunk: Uint8Array): void { - // 如果新数据太大,只接收部分 if (chunk.byteLength > LengthFieldBasedFrameDecoder.MAX_FRAME_SIZE) { - console.warn('[Frame Decoder] Chunk too large, truncating'); - chunk = chunk.slice(0, LengthFieldBasedFrameDecoder.MAX_FRAME_SIZE); + console.error('[Frame Decoder] Chunk size exceeds limit:', { + size: chunk.byteLength, + limit: LengthFieldBasedFrameDecoder.MAX_FRAME_SIZE + }); + throw new Error('Chunk size exceeds maximum frame size'); }Likely invalid or redundant comment.
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/connection/src/common/connection/drivers/frame-decoder.ts (2)
13-20
: 常量定义优化建议新增的常量定义提高了代码的可维护性,但建议考虑以下几点:
MAX_FRAME_SIZE
的大小(1MB)可能需要根据实际使用场景进行调整MAX_ITERATIONS
的值(50)应该添加注释说明选择该值的原因建议添加以下注释:
- private static readonly MAX_FRAME_SIZE = 1 * 1024 * 1024; // 1MB + private static readonly MAX_FRAME_SIZE = 1 * 1024 * 1024; // 1MB - 根据WebSocket最佳实践设置的单帧大小限制 - private static readonly MAX_ITERATIONS = 50; + private static readonly MAX_ITERATIONS = 50; // 单次处理最大迭代次数,防止过度占用CPUAlso applies to: 27-29
54-69
: 异步处理优化值得肯定代码通过以下方式优化了性能:
- 通过
processingPromise
确保同时只有一个处理过程- 每处理10个包就让出执行权,防止长时间占用主线程
- 设置了最大迭代次数,避免死循环
建议考虑添加可配置的处理间隔时间,例如:
+ private static readonly YIELD_INTERVAL = 10; // 每处理多少个包让出执行权 // ... - if (iterations % 10 === 0) { + if (iterations % LengthFieldBasedFrameDecoder.YIELD_INTERVAL === 0) {Also applies to: 71-90
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/connection/src/common/connection/drivers/frame-decoder.ts
(5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: ubuntu-latest, Node.js 20.x
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: build-windows
🔇 Additional comments (1)
packages/connection/src/common/connection/drivers/frame-decoder.ts (1)
30-38
: 监听器管理方式的重大变更这个变更将之前基于
Emitter
的多监听器方法改为了单一监听器方法。虽然这简化了事件处理机制,但也限制了类只能同时处理一个监听器。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (6)
packages/connection/src/common/connection/drivers/frame-decoder.ts (3)
53-82
: 处理队列的实现值得肯定,建议补充日志当前实现通过 Promise 队列和迭代限制很好地解决了处理阻塞问题。建议添加以下改进:
if (iterations % 10 === 0) { + console.debug('[Frame Decoder] Yielding after processing 10 frames'); await new Promise((resolve) => setTimeout(resolve, 0)); }
85-119
: 建议优化内存管理当前实现在处理大量数据时可能会占用较多内存。建议添加以下优化:
const binary = this.buffers.slice(start, end); +// 设置一个合理的缓冲区大小限制 +const MAX_BUFFER_SIZE = 50 * 1024 * 1024; // 50MB +if (this.buffers.byteLength > MAX_BUFFER_SIZE) { + console.warn('[Frame Decoder] Buffer size exceeds limit, clearing'); + this.buffers.clear(); + this.reset(); +}
205-218
: 建议添加输入验证当前实现没有对输入内容进行验证。建议添加:
static construct(content: Uint8Array) { + if (!content || content.byteLength === 0) { + throw new Error('Content cannot be empty'); + } const writer = BinaryWriter({});packages/connection/__test__/common/frame-decoder.test.ts (3)
38-40
: 建议添加更多边界测试用例当前测试用例覆盖了基本场景,建议添加以下边界情况:
- 空数据包
- 最大帧大小的数据包
- 无效的帧长度
74-86
: 建议添加超时处理当前异步测试可能在数据未到达时无限等待。建议添加:
- const result = await new Promise<Uint8Array>((resolve) => { + const result = await new Promise<Uint8Array>((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error('Timeout waiting for data')); + }, 5000); decoder.onData((data) => { + clearTimeout(timeout); resolve(data) });
89-115
: 建议增强测试断言当前测试只验证了数据内容,建议添加以下断言:
- 验证接收顺序
- 验证接收时间间隔
- 验证内存使用情况
receivedData.forEach((data, index) => { fastExpectBufferEqual(data, purePackets[index][1]); + // 验证接收时间间隔不超过100ms + if (index > 0) { + expect(receivedTimes[index] - receivedTimes[index - 1]).toBeLessThan(100); + } });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/connection/__test__/common/frame-decoder.test.ts
(4 hunks)packages/connection/src/common/connection/drivers/frame-decoder.ts
(6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: build-windows
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (1)
packages/connection/src/common/connection/drivers/frame-decoder.ts (1)
29-37
: 监听器管理方式需要重新考虑当前实现将多监听器模式改为单一监听器模式,这可能会限制类的使用场景。
建议:
- 考虑使用数组存储多个监听器
- 添加文档说明当前限制
- 实现
removeListener
方法使 API 更完整
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/connection/src/common/fury-extends/one-of.ts (1)
71-73
: 增强了错误处理机制添加了默认分支来处理无效的索引值,这样可以及时发现序列化过程中的异常情况。
建议进一步改进错误消息,提供更多上下文信息:
- throw new Error('unknown index: ' + idx); + throw new Error(`无效的序列化索引: ${idx},有效范围为 0-${cap - 1}`);packages/connection/__test__/node/index.test.ts (1)
65-66
: 消息处理逻辑重构重构后的消息处理逻辑更加清晰和类型安全:
- 使用了
wrappedConnection
进行序列化处理- 通过
onMessage
方法统一处理消息- 使用了类型化的消息处理机制
建议添加错误处理机制:
wrappedConnection.onMessage((msg: ChannelMessage) => { + try { if (msg.kind === 'server-ready') { if (msg.id === 'TEST_CHANNEL_ID') { channel.dispatch(msg); } } + } catch (error) { + console.error('消息处理失败:', error); + // 可以在这里添加适当的错误处理逻辑 + } });Also applies to: 71-77
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/connection/__test__/node/index.test.ts
(2 hunks)packages/connection/src/common/fury-extends/one-of.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: build-windows
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (1)
packages/connection/__test__/node/index.test.ts (1)
11-11
: 类型导入优化通过导入
ChannelMessage
类型,增强了消息处理的类型安全性。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
packages/connection/src/common/connection/drivers/ws-websocket.ts (2)
32-80
: 建议优化内存使用。当前实现在处理大文件时可能会占用较多内存。建议考虑以下优化:
- 使用
ArrayBuffer
或SharedArrayBuffer
来减少内存复制- 考虑使用
WebSocket.bufferedAmount
来控制发送速率private async processSendQueue() { if (this.sending) return; this.sending = true; while (this.sendQueue.length > 0) { const { data, resolve, reject } = this.sendQueue[0]; let handle: { get: () => Uint8Array; dispose: () => void } | null = null; try { handle = LengthFieldBasedFrameDecoder.construct(data).dumpAndOwn(); const packet = handle.get(); const sendChunk = async (start: number) => { while (start < packet.byteLength && (this.socket as any).bufferedAmount < 1024 * 1024) { if (!this.isOpen()) throw new Error('连接已关闭'); const end = Math.min(start + chunkSize, packet.byteLength); const chunk = packet.subarray(start, end); await new Promise<void>((resolve, reject) => { this.socket.send(chunk, { binary: true }, (error?: Error) => { error ? reject(error) : resolve(); }); }); start = end; } if (start < packet.byteLength) { await new Promise(resolve => setTimeout(resolve, 50)); await sendChunk(start); } }; await sendChunk(0); resolve(); } catch (error) { reject(error instanceof Error ? error : new Error(String(error))); } finally { handle?.dispose(); this.pendingSize -= this.sendQueue[0].data.byteLength; this.sendQueue.shift(); } } this.sending = false; }
114-123
: 建议添加日志记录。在资源清理时添加日志记录有助于问题排查:
dispose(): void { + console.log('[WSWebSocket] 开始清理资源'); this.socket.removeAllListeners(); while (this.sendQueue.length > 0) { const { reject } = this.sendQueue.shift()!; + console.warn('[WSWebSocket] 取消未发送的消息'); reject(new Error('Connection disposed')); } this.pendingSize = 0; this.sending = false; + console.log('[WSWebSocket] 资源清理完成'); }packages/connection/src/common/connection/drivers/frame-decoder.ts (3)
56-94
: 异步处理逻辑的改进建议当前的异步处理实现有以下优点:
- 使用
processingPromise
确保同时只有一个处理过程- 通过
MAX_ITERATIONS
限制避免无限循环- 使用
setImmediate
防止长时间阻塞建议进一步改进错误处理:
this.processingPromise = this.processBuffers().finally(() => { this.processingPromise = null; +}).catch((error) => { + console.error('[Frame Decoder] Error in processBuffers:', error); + this.reset(); });
96-130
: 建议增强错误处理和日志记录当前的错误处理可以通过添加更多上下文信息来改进:
-console.error('[Frame Decoder] Error in data listener:', error); +console.error('[Frame Decoder] Error in data listener:', { + error, + contentLength: this.contentLength, + bufferLength: this.buffers.byteLength, + state: this.state, + cursorOffset: this.cursor.offset +});
216-229
: 建议改进构造函数的错误处理当前的错误处理可以更具体地处理不同类型的错误:
try { writer.buffer(indicator); writer.uint32(content.byteLength); writer.buffer(content); return writer; } catch (error) { - console.warn('[Frame Decoder] Error constructing frame:', error); + if (error instanceof TypeError) { + console.warn('[Frame Decoder] Invalid buffer type:', error); + } else if (error instanceof RangeError) { + console.warn('[Frame Decoder] Buffer size exceeds limits:', error); + } else { + console.warn('[Frame Decoder] Unexpected error constructing frame:', error); + } throw error; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/connection/__test__/node/ws-channel.test.ts
(3 hunks)packages/connection/src/common/connection/drivers/frame-decoder.ts
(6 hunks)packages/connection/src/common/connection/drivers/ws-websocket.ts
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: build-windows
- GitHub Check: ubuntu-latest, Node.js 20.x
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: build (macos-latest, 20.x)
🔇 Additional comments (6)
packages/connection/src/common/connection/drivers/ws-websocket.ts (3)
11-24
: 队列管理的实现看起来不错!接口定义清晰,队列大小限制和状态追踪的实现都很合理。
27-29
: 建议在消息处理中添加错误处理。为了提高代码的健壮性,建议在处理接收到的消息时添加错误处理:
-this.socket.on('message', (data: Buffer) => { - this.decoder.push(data); -}); +this.socket.on('message', (data: Buffer) => { + try { + this.decoder.push(data); + } catch (error) { + console.error('解码器处理数据时发生错误:', error); + // 可以在这里添加重试逻辑或其他错误处理 + } +});
98-100
: 实现简洁有效!使用 decoder 处理消息的方式很好,保持了代码的简洁性。
packages/connection/__test__/node/ws-channel.test.ts (1)
88-88
: 测试用例的改进很合理!
- 添加索引到消息内容增加了测试的多样性
- 增加超时时间适应了分块发送的实现
这些改动使测试更加健壮。
Also applies to: 98-98, 152-152
packages/connection/src/common/connection/drivers/frame-decoder.ts (2)
29-37
: 监听器管理方式的重大变更这个变更将之前基于
Emitter
的多监听器方法改为了单一监听器方法。虽然这简化了事件处理机制,但也限制了类只能同时处理一个监听器。建议考虑使用数组来存储多个监听器,以提供更大的灵活性。
13-20
: 🛠️ Refactor suggestion建议添加最大帧大小常量
为了防止处理过大的数据帧导致内存问题,建议添加最大帧大小常量:
+/** + * 最大帧大小限制(例如:10MB) + */ +const MAX_FRAME_SIZE = 10 * 1024 * 1024;Likely invalid or redundant comment.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3988 +/- ##
==========================================
+ Coverage 53.83% 53.86% +0.03%
==========================================
Files 1650 1650
Lines 101511 101648 +137
Branches 21968 21991 +23
==========================================
+ Hits 54648 54753 +105
- Misses 38966 38995 +29
- Partials 7897 7900 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
/next |
🎉 PR Next publish successful! 3.7.1-next-1739178290.0 |
/next |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/file-service/src/node/hosted/watcher.host.service.ts (3)
139-141
: 建议增加错误处理机制异步操作的引入提高了代码的健壮性,但建议添加 try-catch 块来处理潜在的异步错误。
建议修改为:
Disposable.create(async () => { - await this.unrecursiveFileSystemWatcher!.unwatchFileChanges(uri.toString()); - this.logger.log('dispose unrecursive watcher: ', uri.toString()); + try { + await this.unrecursiveFileSystemWatcher!.unwatchFileChanges(uri.toString()); + this.logger.log('dispose unrecursive watcher: ', uri.toString()); + } catch (error) { + this.logger.error('Failed to unwatch file changes: ', error); + } this.watchedDirs.delete(uri.toString()); this.WATCHER_HANDLERS.delete(watcherId); }),
156-158
: 建议增加错误处理机制与非递归监视器类似,递归监视器的异步操作也需要错误处理机制。
建议修改为:
Disposable.create(async () => { - this.logger.log('dispose recursive watcher: ', uri.toString()); - await this.recursiveFileSystemWatcher!.unwatchFileChanges(uri.toString()); + try { + this.logger.log('dispose recursive watcher: ', uri.toString()); + await this.recursiveFileSystemWatcher!.unwatchFileChanges(uri.toString()); + } catch (error) { + this.logger.error('Failed to unwatch recursive changes: ', error); + } this.watchedDirs.delete(uri.toString()); this.WATCHER_HANDLERS.delete(watcherId); }),
110-174
: 建议优化大文件处理机制根据PR的目标(通过分块发送来处理大文件),建议在文件监视器中也实现相应的优化。当监视的是大文件时,可能需要特殊处理以避免性能问题。
建议考虑以下几点:
- 为大文件设置单独的处理策略
- 实现文件大小检查
- 针对大文件可以采用节流(throttle)机制来减少事件触发频率
需要您确认是否需要我提供具体的实现方案?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/file-service/src/node/hosted/watcher.host.service.ts
(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: build-windows
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (1)
packages/file-service/src/node/hosted/watcher.host.service.ts (1)
82-82
: 优化监视器集合的清理时机在重新监视之前删除监视器,可以防止潜在的竞态条件。
🎉 PR Next publish successful! 3.7.1-next-1739181952.0 |
/next |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/file-service/src/node/hosted/recursive/file-service-watcher.ts (1)
261-264
: 路径处理优化建议代码改动通过使用
FileUri.fsPath(realPath).toString()
统一了路径格式,这是一个好的改进。不过建议进一步优化:
- 考虑将路径格式化逻辑抽取为一个工具函数,以确保整个代码库中路径处理的一致性
- 可以在
WATCHER_HANDLERS
的类型定义中明确键的类型为string
,以更好地表达意图- private WATCHER_HANDLERS = new Map< - string, - { path: string; handlers: ParcelWatcher.SubscribeCallback[]; disposable: IDisposable } - >(); + private normalizeWatchPath(path: string): string { + return FileUri.fsPath(path).toString(); + } + + private WATCHER_HANDLERS = new Map< + string, + { path: string; handlers: ParcelWatcher.SubscribeCallback[]; disposable: IDisposable } + >();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/file-service/src/node/hosted/recursive/file-service-watcher.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
- GitHub Check: 🚀🚀🚀 Next Version for pull request
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: ubuntu-latest, Node.js 20.x
- GitHub Check: build-windows
🎉 PR Next publish successful! 3.7.1-next-1739183195.0 |
/next |
🎉 PR Next publish successful! 3.7.1-next-1739183529.0 |
c76f711
to
1ae5587
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
packages/connection/__test__/common/frame-decoder.test.ts (2)
74-86
: 改进了流解码测试的实现方式
- 使用 async/await 替代回调,提高了代码可读性
- 添加了
dispose()
确保资源正确释放- 通过 Promise 优化了异步流程控制
117-232
: 新增全面的边界场景测试新增测试覆盖了以下关键场景:
- 分块传输(验证指示符分割情况)
- 高频小数据包(验证性能和稳定性)
- 内存稳定性(验证内存泄漏)
- 空数据包处理
- 并发推送
这些测试用例显著提高了代码质量和可靠性。建议考虑添加:
- 极限并发场景测试
- 网络延迟模拟
- 错误恢复机制测试
packages/connection/__test__/common/buffers.test.ts (3)
269-280
: 建议增加更多边界情况的测试当前的边界测试仅覆盖了chunk边界的开始和结束位置,建议添加以下场景:
- 跨多个chunk的边界切片
- 空chunk的处理
- 最大chunk大小的边界值
describe('Edge Case Slicing', () => { const buffer = create([0, 1, 2, 3, 4, 5], [3, 3]); it('should handle start at chunk boundary', () => { expect(buffer.slice(3, 5)).toEqual(new Uint8Array([3, 4])); }); it('should handle end at chunk boundary', () => { expect(buffer.slice(2, 3)).toEqual(new Uint8Array([2])); }); + + it('should handle cross-chunk slicing', () => { + const multiChunk = create([0, 1, 2, 3, 4, 5], [2, 2, 2]); + expect(multiChunk.slice(1, 4)).toEqual(new Uint8Array([1, 2, 3])); + }); + + it('should handle empty chunks', () => { + const withEmpty = create([0, 1, 2], [1, 0, 2]); + expect(withEmpty.slice(0, 3)).toEqual(new Uint8Array([0, 1, 2])); + }); });
368-388
: 性能测试需要更严格的断言当前的性能测试仅验证了执行时间,建议添加内存使用的监控。
it('should handle slicing 1MB data under 50ms', () => { + const startHeap = process.memoryUsage().heapUsed; const start = performance.now(); const slice = largeBuffer.slice(0, 1024 * 1024); const duration = performance.now() - start; + const heapUsed = process.memoryUsage().heapUsed - startHeap; expect(duration).toBeLessThan(50); expect(slice.byteLength).toBe(1024 * 1024); + expect(heapUsed).toBeLessThan(2 * 1024 * 1024); // 确保内存使用不超过2MB });
401-412
: createEnhanced函数的错误处理可以更完善建议添加对负数chunk大小的明确错误提示。
function createEnhanced(xs: number[], split: number[]): Buffers { + if (split.some(size => size < 0)) { + throw new Error('Chunk size cannot be negative'); + } const bufs = new Buffers(); let offset = 0; split.forEach((chunkSize) => { if (chunkSize > 0) { const chunk = new Uint8Array(xs.slice(offset, offset + chunkSize)); bufs.push(chunk); offset += chunkSize; } }); return bufs; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/connection/__test__/common/buffers.test.ts
(2 hunks)packages/connection/__test__/common/frame-decoder.test.ts
(4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: build-windows
- GitHub Check: ubuntu-latest, Node.js 20.x
- GitHub Check: unittest (macos-latest, 18.x, node)
🔇 Additional comments (4)
packages/connection/__test__/common/frame-decoder.test.ts (3)
38-40
: 优化了数据包构造方式通过统一使用
.dump()
方法序列化数据包,确保了数据包格式的一致性。
64-64
: 测试用例更新符合新的数据包构造标准使用
.dump()
方法确保测试数据的一致性和可靠性。
89-115
: 新增多帧处理测试用例完善了以下测试场景:
- 多帧数据的顺序接收
- 数据完整性验证
- 资源清理
建议添加以下场景的测试:
- 帧大小边界条件
- 错误的帧格式处理
packages/connection/__test__/common/buffers.test.ts (1)
257-267
: 测试用例覆盖了空缓冲区的基本操作测试用例完整地验证了空缓冲区的行为,包括长度、切片和位置查询等操作。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts (2)
127-130
:⚠️ Potential issue需要添加错误处理机制
当前的
arrayBufferHandler
实现缺少错误处理,可能在解码失败时导致程序崩溃。建议添加错误处理:
private arrayBufferHandler = (e: MessageEvent<ArrayBuffer>) => { - const buffer: ArrayBuffer = e.data; - this.decoder.push(new Uint8Array(buffer, 0, buffer.byteLength)); + try { + const buffer: ArrayBuffer = e.data; + this.decoder.push(new Uint8Array(buffer, 0, buffer.byteLength)); + } catch (error) { + console.error('[ReconnectingWebSocket] 处理 ArrayBuffer 消息时出错:', error); + this.emit('error', error); + } };
133-135
:⚠️ Potential issue需要正确处理队列中的待处理 Promise
在
dispose
方法中,直接清空队列可能导致未处理的 Promise 永远挂起。建议在清空队列前拒绝所有待处理的 Promise:
dispose(): void { this.socket.removeEventListener('message', this.arrayBufferHandler); + // 拒绝所有待处理的 Promise + while (this.sendQueue.length > 0) { + const { reject } = this.sendQueue.shift()!; + reject(new Error('连接已关闭')); + } - this.sendQueue = []; this.sending = false; }
🧹 Nitpick comments (9)
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts (3)
23-27
: 建议增强二进制类型处理的错误信息当
binaryType
为 'blob' 时抛出的错误信息过于简单。建议提供更详细的错误信息,包括支持的类型和解决方案。建议修改为:
- throw new Error('blob is not implemented'); + throw new Error('WebSocket binary type "blob" is not supported. Please use "arraybuffer" instead.');
44-50
: 建议优化分块发送的错误处理和性能监控当前的分块发送实现缺少重试机制和性能监控。
建议添加以下功能:
- 重试机制
- 发送进度回调
- 性能指标收集
+ private async sendChunk(chunk: Uint8Array, retries = 3): Promise<void> { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + await new Promise<void>((resolve, reject) => { + const start = performance.now(); + this.socket.send(chunk); + const duration = performance.now() - start; + console.debug(`Chunk sent in ${duration}ms`); + resolve(); + }); + return; + } catch (error) { + if (attempt === retries) throw error; + await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); + } + } + } for (let i = 0; i < packet.byteLength; i += chunkSize) { - await new Promise<void>((resolve) => { - const chunk = packet.subarray(i, Math.min(i + chunkSize, packet.byteLength)); - this.socket.send(chunk); - resolve(); - }); + const chunk = packet.subarray(i, Math.min(i + chunkSize, packet.byteLength)); + await this.sendChunk(chunk); + this.emit('progress', { + sent: i + chunk.byteLength, + total: packet.byteLength + }); }
15-19
: 建议添加类级别的文档注释作为一个重要的连接类,建议添加详细的类级别文档,说明其功能、使用方式和注意事项。
+/** + * 实现了具有重连功能的 WebSocket 连接类 + * + * 特性: + * - 支持自动重连 + * - 支持大文件分块发送 + * - 使用 LengthFieldBasedFrameDecoder 处理消息 + * + * @example + * ```typescript + * const connection = ReconnectingWebSocketConnection.forURL('ws://example.com'); + * connection.onMessage((data) => console.log('收到消息:', data)); + * await connection.send(new Uint8Array([1, 2, 3])); + * ``` + */ export class ReconnectingWebSocketConnection extends BaseConnection<Uint8Array> {packages/connection/__test__/common/buffers.test.ts (6)
270-285
: 建议改进测试结构将
should handle splice at exact chunk boundary
测试用例移入Edge Case Slicing
describe 块内,以保持测试结构的一致性。describe('Edge Case Slicing', () => { const buffer = create([0, 1, 2, 3, 4, 5], [3, 3]); it('should handle start at chunk boundary', () => { expect(buffer.slice(3, 5)).toEqual(new Uint8Array([3, 4])); }); it('should handle end at chunk boundary', () => { expect(buffer.slice(2, 3)).toEqual(new Uint8Array([2])); }); + + it('should handle splice at exact chunk boundary', () => { + const buffer = createEnhanced([0, 1, 2, 3, 4, 5], [3, 3]); + buffer.splice(3, 2, new Uint8Array([99])); + expect(buffer.slice()).toEqual(new Uint8Array([0, 1, 2, 99, 5])); + }); }); -it('should handle splice at exact chunk boundary', () => { - const buffer = createEnhanced([0, 1, 2, 3, 4, 5], [3, 3]); - buffer.splice(3, 2, new Uint8Array([99])); - expect(buffer.slice()).toEqual(new Uint8Array([0, 1, 2, 99, 5])); -});
330-340
: 建议添加空缓冲区的slice4测试为确保完整的测试覆盖率,建议添加对空缓冲区调用slice4方法的测试用例。
describe('slice4 Special Cases', () => { + it('should handle slice4 on empty buffer', () => { + const buffer = new Buffers(); + expect(buffer.slice4(0)).toEqual(new Uint8Array([0, 0, 0, 0])); + }); + it('should handle partial slice4', () => { const buffer = create([1, 2, 3], [3]); expect(buffer.slice4(2)).toEqual(new Uint8Array([3, 0, 0, 0])); });
343-359
: 建议添加错误情况测试Cursor高级操作测试应该包含错误情况的处理,比如:
- 移动到负偏移量
- 移动到超出范围的位置
describe('Cursor Advanced Operations', () => { const buffer = create([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [3, 3, 4]); + it('should throw on negative moveTo', () => { + const cursor = buffer.cursor(); + expect(() => cursor.moveTo(-1)).toThrow('out of range'); + }); + + it('should throw on overflow moveTo', () => { + const cursor = buffer.cursor(); + expect(() => cursor.moveTo(buffer.byteLength + 1)).toThrow('out of range'); + });
362-370
: 建议增强资源清理验证建议添加内存泄漏检测,确保所有内部引用都被正确清理。
describe('Resource Management', () => { it('should clear resources on dispose', () => { const buffer = create([1, 2, 3], [3]); + const cursor = buffer.cursor(); buffer.dispose(); expect(buffer.buffers).toEqual([]); expect(buffer.byteLength).toBe(0); + expect(() => cursor.value).toThrow(); // 确保游标引用被清理 }); });
373-407
: 建议优化性能测试阈值设置建议将性能测试的时间阈值设置为可配置的参数,以适应不同的测试环境:
- CI/CD环境可能需要更宽松的阈值
- 本地开发环境可以使用更严格的阈值
+const PERFORMANCE_THRESHOLDS = { + SLICE_THRESHOLD_MS: process.env.SLICE_THRESHOLD_MS || 50, + SPLICE_THRESHOLD_MS: process.env.SPLICE_THRESHOLD_MS || 1000, +}; + describe('Performance Tests', () => { // ... it('should handle slicing 1MB data under 50ms', () => { const start = performance.now(); const slice = largeBuffer.slice(0, 1024 * 1024); const duration = performance.now() - start; - expect(duration).toBeLessThan(50); + expect(duration).toBeLessThan(PERFORMANCE_THRESHOLDS.SLICE_THRESHOLD_MS); expect(slice.byteLength).toBe(1024 * 1024); });
420-431
: 建议添加输入参数验证
createEnhanced
函数应该验证 split 数组的有效性:
- 确保 split 数组不为空
- 验证 split 数组的和不超过 xs 数组的长度
function createEnhanced(xs: number[], split: number[]): Buffers { + if (!split.length) { + throw new Error('Split array cannot be empty'); + } + const totalSize = split.reduce((sum, size) => sum + size, 0); + if (totalSize > xs.length) { + throw new Error('Split sizes exceed source array length'); + } const bufs = new Buffers(); let offset = 0; split.forEach((chunkSize) => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/connection/__test__/common/buffers.test.ts
(2 hunks)packages/connection/src/common/connection/drivers/reconnecting-websocket.ts
(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: unittest (macos-latest, 18.x, jsdom)
- GitHub Check: unittest (macos-latest, 18.x, node)
- GitHub Check: ubuntu-latest, Node.js 20.x
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: build (macos-latest, 20.x)
- GitHub Check: build-windows
🔇 Additional comments (6)
packages/connection/src/common/connection/drivers/reconnecting-websocket.ts (2)
67-71
: 实现良好,已修复之前的 Promise 处理问题新的实现正确处理了 Promise 的 resolve 和 reject,并通过队列确保了消息的有序发送。
86-88
: 消息处理逻辑优化得当使用 decoder 处理消息的方式更加清晰和可维护。
packages/connection/__test__/common/buffers.test.ts (4)
258-267
: 测试用例设计合理!空缓冲区测试用例覆盖了关键的边界情况,包括长度检查、切片操作和越界访问。
287-297
: 错误处理测试设计完善!非法索引访问测试用例很好地覆盖了负索引和越界索引的场景。
299-315
: 性能测试中的内存使用需要优化当前的大缓冲区测试创建了1MB的连续数组,这可能导致内存压力。建议使用更小的块大小进行测试。
318-327
: 跨块操作测试设计合理!跨chunk拼接测试用例很好地验证了修改后的缓冲区内容和被移除的元素。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/connection/__test__/common/frame-decoder.test.ts (2)
74-87
: 测试代码现代化改进将回调式的测试模式改为使用 async/await,提高了代码的可读性和可维护性。这种方式更容易处理异步操作,并且能更好地处理错误情况。
132-232
: 全面的边缘场景测试补充新增了多个重要的测试场景,显著提升了测试覆盖率:
- 分块传输测试(132-154行):验证解码器对分片数据的处理能力
- 高频小数据包测试(157-177行):验证解码器在高压力下的稳定性
- 内存泄漏测试(180-199行):确保长期运行时的内存稳定性
- 空载荷测试(202-212行):验证对边界情况的处理
- 并发推送测试(215-232行):验证并发场景下的正确性
这些测试用例的添加极大地提高了代码的可靠性。
建议考虑添加以下场景的测试:
- 错误数据的处理
- 超大帧的处理限制
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/connection/__test__/common/frame-decoder.test.ts
(4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: unittest (ubuntu-latest, 18.x, jsdom)
- GitHub Check: build (ubuntu-latest, 20.x)
- GitHub Check: unittest (ubuntu-latest, 18.x, node)
- GitHub Check: build-windows
- GitHub Check: ubuntu-latest, Node.js 20.x
🔇 Additional comments (2)
packages/connection/__test__/common/frame-decoder.test.ts (2)
38-40
: 优化了数据包的构造方式通过在构造时立即调用
.dump()
,确保了测试数据的一致性和可靠性。这种方式更符合实际使用场景,因为在实际应用中数据包总是会被序列化的。
89-116
: 新增多帧处理测试新增了对多帧数据的处理测试,这对于验证解码器在处理连续数据流时的正确性非常重要。测试用例设计合理,覆盖了以下关键点:
- 多帧数据的正确解析
- 数据完整性验证
- 内存资源的及时释放
/next |
🎉 PR Next publish successful! 3.7.1-next-1740016426.0 |
🎉 PR Next publish successful! 3.7.2-next-1740016705.0 |
Types
Background or solution
一次性发送几十M的文件会导致线程卡顿,大文件分 chunk 发送
Changelog
Summary by CodeRabbit
Refactor
New Features