Skip to content

storage: limit fruitless SearchV2 metabase iterations#3915

Open
End-rey wants to merge 1 commit intomasterfrom
3172-searchv2-dos-protection
Open

storage: limit fruitless SearchV2 metabase iterations#3915
End-rey wants to merge 1 commit intomasterfrom
3172-searchv2-dos-protection

Conversation

@End-rey
Copy link
Copy Markdown
Contributor

@End-rey End-rey commented Mar 27, 2026

Closes #3172.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 64.28571% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 26.80%. Comparing base (7faca4f) to head (8f41303).

Files with missing lines Patch % Lines
...eofs-node/config/engine/shard/metabase/metabase.go 0.00% 9 Missing ⚠️
cmd/neofs-node/storage.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3915   +/-   ##
=======================================
  Coverage   26.80%   26.80%           
=======================================
  Files         677      677           
  Lines       44708    44732   +24     
=======================================
+ Hits        11983    11992    +9     
- Misses      31613    31627   +14     
- Partials     1112     1113    +1     

☔ 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.

Perm fs.FileMode `mapstructure:"perm"`
MaxBatchSize internal.Size `mapstructure:"max_batch_size"`
MaxBatchDelay time.Duration `mapstructure:"max_batch_delay"`
SearchIterationLimit internal.Size `mapstructure:"search_iteration_limit"`
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.

Is it possible to have zero internal.Size?

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.

Actually, didn't notice. Changed it slightly to a different semantics with -1 for no limit.

| `perm` | file mode | `0640` | Permissions to set for the database file. |
| `max_batch_size` | `int` | `1000` | Maximum amount of write operations to perform in a single transaction. |
| `max_batch_delay` | `duration` | `10ms` | Maximum delay before a batch starts. |
| `search_iteration_limit` | `int` | `10000` | Maximum number of objects to be iterated without a match during search operations. |
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.

Behavior for zero should be specified as well.

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.

Added.

// [ErrSearchIterationLimitExceeded].
func WithSearchIterationLimit(limit uint64) Option {
return func(c *cfg) {
if limit != 0 {
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.

How can I disable this behavior then? The default is 10K, 0 isn't allowed.

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.

Now allowed.

if !handleKV(primKey, nil) {
break
}
if db.searchIterationLimit > 0 && iterations >= db.searchIterationLimit && len(resHolder.Objects) == 0 {
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.

Not sure it's effective this way, maybe some request can be created to have a single element and then still make iterations for all the other cases. I'd rather count iterations between resHolder.Objects increments (or not), resetting the counter when an object is added. However this can also be abused to try 10K*1000 iterations. But it should be harder to do that and then iteration count can be reduced.

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.

Done.

@End-rey End-rey force-pushed the 3172-searchv2-dos-protection branch from 58dc315 to 2f29338 Compare April 1, 2026 15:39
@End-rey End-rey requested a review from roman-khimov April 1, 2026 15:42
@End-rey End-rey force-pushed the 3172-searchv2-dos-protection branch from 2f29338 to 8f41303 Compare April 1, 2026 15:45
Add `storage.shard.metabase.search_iteration_limit` for filtered SearchV2, -1
means no limit, 0 means default. Abort metabase scans after the configured
number of consecutive iterations without a match.

Closes #3172.

Signed-off-by: Andrey Butusov <andrey@nspcc.io>
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.

SearchV2 DOS protection

2 participants