Skip to content

sn/object: Optimize EC GET handling without recovery#3996

Open
cthulhu-rider wants to merge 1 commit into
masterfrom
ec-get
Open

sn/object: Optimize EC GET handling without recovery#3996
cthulhu-rider wants to merge 1 commit into
masterfrom
ec-get

Conversation

@cthulhu-rider
Copy link
Copy Markdown
Contributor

Closes #3952.

Comment thread cmd/neofs-node/object.go
keys: keyStorage,
}
server := objectService.New(objSvc, mNumber, c.cfgObject.pool.search, fsChain, storage, c.metaService, c.key.PrivateKey, c.metricsCollector, aclChecker, aclSvc, coreConstructor)
server := objectService.New(objSvc, mNumber, c.cfgObject.pool.search, fsChain, storage, c.metaService, c.key.PrivateKey, c.metricsCollector, aclChecker, aclSvc, coreConstructor, c.log)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes #3098?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, i did not touch those TODOs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But related for sure.

Comment thread pkg/core/client/client.go
//
// If next endpoint is unavailable or f returns [ErrSkipConnection] for it,
// ForAnyGRPCConn continues. If this happens on all endpoints, ForAnyGRPCConn
// returns [ErrAllConnectionsSkipped].
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change can be separated from a single big commit.

if errors.Is(err, ctx.Err()) {
return false, 0, 0, 0, err
}
// TODO: if error is due to incorrect request, error should be returned. How to catch this?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to #3413, container node checks the original request then supposedly it always creates perfect new ones (so that any error in that requests is kinda an internal error to client).

if chunkFrom > 0 {
_, ok := fld.MoveNext(chunkFrom)
if !ok {
return 0, fmt.Errorf("%w while moving to offset=%d in chunk with length=%d", io.ErrUnexpectedEOF, chunkFrom, chunkLen)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

respBuf.Free?

var ok bool
fld, ok = fld.MoveNext(chunkTo - chunkFrom)
if !ok {
return 0, fmt.Errorf("%w while moving to offset=%d in chunk with length=%d", io.ErrUnexpectedEOF, chunkTo-chunkFrom, chunkLen-chunkFrom)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well.

Comment thread pkg/services/object/get.go Outdated
}

if controlCh != nil {
if controlCh != nil {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Twice to be sure?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah yes

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes EC GET handling by attempting to stream EC parts directly to the client before falling back to full EC recovery, reducing latency and intermediate buffering.

Changes:

  • Adds EC GET transport logic for local/remote EC part streaming and range forwarding.
  • Adds low-level protobuf helpers for request/response construction and object header parsing.
  • Updates multi-address client retry semantics to support explicitly skipped connections.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/services/object/util.go Extracts split-info parsing and adds partial-copy read error metadata.
pkg/services/object/server.go Wires EC transport into GET and generalizes stream-copy helpers.
pkg/services/object/proto.go Adds fixed-length protobuf/signature helpers for manual request construction.
pkg/services/object/get/util.go Adds partial EC copy error type.
pkg/services/object/get/service.go Defines EC transport interface and shared stream-control errors.
pkg/services/object/get/prm.go Adds EC transport parameter to GET parameters.
pkg/services/object/get/get.go Passes EC transport into EC object copy path.
pkg/services/object/get/exec.go Uses exported stream failure sentinel.
pkg/services/object/get/ec.go Adds direct EC part streaming orchestration before recovery fallback.
pkg/services/object/get.go Implements EC part request creation and remote/local streaming transport.
pkg/network/cache/clients.go Adds skip-connection handling and structured multi-endpoint errors.
pkg/network/cache/clients_internal_test.go Tests multi-endpoint error wrapping behavior.
pkg/core/client/client.go Adds connection-skip sentinel errors to the multi-address client API.
internal/protobuf/tags.go Adds protobuf byte tag constants for fields 7–9.
internal/protobuf/tags_test.go Extends tag constant validation.
internal/protobuf/seekers.go Adds enum field lookup helper.
internal/protobuf/api.go Adds fixed protobuf lengths for container IDs and object addresses.
internal/protobuf/api_test.go Tests fixed protobuf message length constants.
internal/object/wire.go Adds object header helpers for parent fields, payload length, and type.
cmd/neofs-node/object.go Passes logger into object service construction.
CHANGELOG.md Records EC GET optimization in changelog.
Comments suppressed due to low confidence (4)

pkg/services/object/get.go:842

  • This break exits only the switch, not the surrounding receive loop. After the requested range is copied, the code keeps reading the rest of the GET stream and can forward extra empty chunks; return copied or use a labeled break here.
			if copied == ln {
				break

pkg/services/object/get.go:1080

  • handleRangeResponseBodyOneof returns FieldRangeResponseBodySplitInfo, so this FieldGetResponseBodySplitInfo case is never reached for RANGE split-info responses. Use the RANGE split-info constant; otherwise split-info responses fall into the default unsupported-oneof error path.
		case protoobject.FieldGetResponseBodySplitInfo:
			respBuf.Free()
			if !first {
				return 0, errors.New("received split info in non-first message")
			}
			return 0, errors.New("unexpected split info status response")

pkg/services/object/get.go:587

  • The retry for a partially copied first EC part requests parentPldLen-copiedPartPld bytes from part #0, but the range length must be limited to the remaining bytes in that part (partPldLen-copiedPartPld). For multi-part EC objects this can request past the part boundary and produce an out-of-range response after data has already been streamed.
		copiedFromNode, err := x.copyRemotePartRange(ctx, conn, ruleIdx, partIdx, copiedPartPld, parentPldLen-copiedPartPld, nil)

pkg/services/object/get/ec.go:1547

  • The local retry path has the same boundary issue: it requests parentPldLen-copiedPartPld bytes from a single EC part instead of the remaining part length. This can overrun the part payload for objects split across multiple data parts; use partPldLen-copiedPartPld here.
			copiedFromNode, err = transport.CopyLocalECPartRange(ctx, s.localObjects.(*engine.StorageEngine), ruleIdx, 0, copiedPartPld, parentPldLen-copiedPartPld, nil)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/services/object/get.go
Comment thread pkg/services/object/get/ec.go
@cthulhu-rider cthulhu-rider force-pushed the ec-get branch 2 times, most recently from 4b1ce3e to c899158 Compare May 19, 2026 08:58
@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

❌ Patch coverage is 5.45113% with 1006 lines in your changes missing coverage. Please review.
✅ Project coverage is 27.80%. Comparing base (84ededb) to head (56d672a).

Files with missing lines Patch % Lines
pkg/services/object/get.go 2.55% 719 Missing and 5 partials ⚠️
pkg/services/object/get/ec.go 2.33% 165 Missing and 2 partials ⚠️
pkg/services/object/proto.go 0.00% 46 Missing ⚠️
pkg/services/object/server.go 41.17% 19 Missing and 1 partial ⚠️
pkg/network/cache/clients.go 40.90% 13 Missing ⚠️
internal/object/wire.go 40.00% 9 Missing ⚠️
internal/protobuf/seekers.go 0.00% 8 Missing ⚠️
pkg/services/object/util.go 33.33% 5 Missing and 3 partials ⚠️
pkg/services/object/get/service.go 0.00% 5 Missing ⚠️
pkg/services/object/get/util.go 0.00% 3 Missing ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3996      +/-   ##
==========================================
- Coverage   28.33%   27.80%   -0.54%     
==========================================
  Files         681      681              
  Lines       45753    46760    +1007     
==========================================
+ Hits        12964    13001      +37     
- Misses      31556    32520     +964     
- Partials     1233     1239       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cthulhu-rider cthulhu-rider force-pushed the ec-get branch 3 times, most recently from 183f040 to 513ee29 Compare May 19, 2026 13:49
Closes #3952. Refs #3098,

Signed-off-by: cthulhurider <ctulhurider@gmail.com>
@cthulhu-rider cthulhu-rider marked this pull request as ready for review May 21, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stream EC parts directly if/when possible

3 participants