Add IPV4_PREFIX_BITS and IPV6_PREFIX_BITS options to keyed_by_ip#89270
Conversation
|
Workflow [PR], commit [5a7dad2] Summary: ✅
AI ReviewSummaryThis PR adds optional Missing context / blind spots
Final VerdictStatus: ✅ Approve |
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
Signed-off-by: Aditya Chopra <adityachopra2912@gmail.com>
479f04e to
c8549af
Compare
|
Hi @alexey-milovidov, any update on this pr's review? I was not able to clear the formatting issues, if any, since there are already a lot of changes that arise besides my additions when I run clang-format. Please tell if any other changes are required. |
|
Hi @alexey-milovidov, Is there any update on this PR? Please advise; I am keen to contribute further in ClickHouse. I will appreciate any feedback to merge this pr. |
|
It didn't pass the style check. |
- Merge with origin/master to resolve conflicts (make_intrusive migration) - Fix style check: remove unnecessary includes, fix trailing whitespace, fix missing space after `if`, fix Allman-style braces, use angle bracket includes instead of quotes, fix `//` comments to `///` - Fix `std::optional<MaskBits>` default from `0` to `std::nullopt` in `Quota.h` so that quotas without explicit prefix bits don't spuriously show them - Fix `formatKeyType` to handle `FORWARDED_IP_ADDRESS` in addition to `IP_ADDRESS` - Fix `parseIpPrefixBits` to use `std::optional<MaskBits>` parameters so only actually parsed prefix bits are set, enabling independent ALTER of each - Fix implicit UInt64→UInt8 truncation before range check in parser - Update test reference file accordingly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The new `Nullable(UInt8)` columns expose the IP address prefix bits configuration for quotas. They are NULL when not configured. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update `02117_show_create_table_system.reference` for new `ipv4_prefix_bits` and `ipv6_prefix_bits` columns in `system.quotas` - Register `IPV4_PREFIX_BITS` and `IPV6_PREFIX_BITS` in the keyword enum instead of using `createDeprecated` - Add prefix bits support for `keyed_by_forwarded_ip` in XML config (was only supported for `keyed_by_ip`) - Fix test `01600_quota_by_prefix_forwarded_ip` to be robust against both IPv4 and IPv6 localhost connections - Fix trailing whitespace in test script Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `test_quota` integration test used `SELECT * FROM system.quotas` which broke after adding `ipv4_prefix_bits` and `ipv6_prefix_bits` columns. Use explicit column names instead to avoid depending on the full schema. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pufit
left a comment
There was a problem hiding this comment.
Nice feature — subnet-level quota keying is a solid addition. The core implementation (parser, serialization round-trip, system table, masking logic) looks good. Found a few issues though, one of which is a real semantic bug.
Bug: Prefix bits not cleared when key_type changes
InterpreterCreateQuotaQuery.cpp only sets prefix bits when the query specifies them — it never clears them:
if (query.ipv4_prefix_bits)
quota.ipv4_prefix_bits = query.ipv4_prefix_bits;This means changing key_type away from IP preserves stale bits:
CREATE QUOTA q KEYED BY ip_address IPV4_PREFIX_BITS 24 IPV6_PREFIX_BITS 64;
ALTER QUOTA q KEYED BY client_key;
-- SHOW CREATE hides the bits (formatter only shows them for IP key types)
-- But system.quotas still shows ipv4_prefix_bits=24, ipv6_prefix_bits=64
ALTER QUOTA q KEYED BY ip_address; -- switch back, no bits specified
-- SHOW CREATE now shows IPV4_PREFIX_BITS 24 IPV6_PREFIX_BITS 64 — ghost values resurrectedThe bits silently survive a round-trip through a non-IP key type. Fix: when query.key_type is set and is not IP_ADDRESS/FORWARDED_IP_ADDRESS, explicitly reset both:
if (query.key_type)
{
quota.key_type = *query.key_type;
if (*query.key_type != QuotaKeyType::IP_ADDRESS
&& *query.key_type != QuotaKeyType::FORWARDED_IP_ADDRESS)
{
quota.ipv4_prefix_bits.reset();
quota.ipv6_prefix_bits.reset();
}
}Same issue in the parser: ALTER QUOTA q IPV4_PREFIX_BITS 16 (without KEYED BY) succeeds even if the quota is keyed by user_name. The bits get persisted on an incompatible key type — system.quotas shows them, SHOW CREATE hides them.
Minor: IPV4_PREFIX_BITS 0 is a no-op at runtime
QuotaCache.cpp has:
if (quota->ipv4_prefix_bits && *quota->ipv4_prefix_bits > 0)So IPV4_PREFIX_BITS 0 is accepted, persisted, displayed in SHOW CREATE — but silently does nothing. A /0 prefix means "all IPs share one bucket" which is valid behavior. Either support it (remove the > 0 check) or reject it in the parser.
Tested all of the above against the arm_release CI build (bb26c10b).
|
|
Hi, I will look into the errors and complete this pr shortly. |
The SQL path rejects `IPV4_PREFIX_BITS`/`IPV6_PREFIX_BITS` on a quota whose key type is not `ip_address` or `forwarded_ip_address`, but the `users.xml` parser silently ignored the same elements for `keyed` or default quotas. Make `UsersConfigParser` fail the config load instead, matching the SQL behaviour, and cover one valid and one rejected XML case in the `test_quota` integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `KEYED BY` syntax blocks and key lists omitted `forwarded_ip_address` even though the prefix-bits paragraph below references it. Add it so the forwarded-IP keying form is discoverable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…masking `ALTER QUOTA q IPV4_PREFIX_BITS 16` without `KEYED BY` exercises the `key_type == std::nullopt` branch of `ASTCreateQuotaQuery::formatImpl`, which is the path used by `ON CLUSTER` distribution; add a round-trip regression for it. Also add forwarded IPv6 masking coverage (`/64` and `/0`) so regressions in the IPv6 branch are caught. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Dear @pufit, you haven't been active on this PR for 30 days. You will be unassigned. Will you continue working on it? If so, please feel free to reassign yourself. |
An IPv4-mapped IPv6 address such as `::ffff:192.0.2.10` represents an IPv4 client, but `Poco::Net::IPAddress::family` reports it as IPv6, so it fell into the IPv6 branch of `QuotaInfo::calculateKey` and was masked by `IPV6_PREFIX_BITS` instead of `IPV4_PREFIX_BITS`. With a wide enough IPv6 prefix this collapsed all mapped IPv4 clients into a single bucket, breaking the contract that IPv4 clients are grouped by `IPV4_PREFIX_BITS`. Normalize an IPv4-mapped address to native IPv4 before masking, so such clients share quota buckets with plain IPv4 clients. Added a regression to `01600_quota_by_prefix_forwarded_ip` exercising `::ffff:1.2.3.4` via the forwarded-IP path, asserting it masks to `1.2.0.0`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `keyed_by_forwarded_ip` users.xml branch is a separate config path from `keyed_by_ip` and from the SQL parser, but only `keyed_by_ip` had XML regression coverage, and the XML quota guide did not mention the prefix-bit elements. Add a valid `<keyed_by_forwarded_ip>` XML case with `<ipv4_prefix_bits>` / `<ipv6_prefix_bits>` to the `test_quota` integration test, and document both elements (and `<keyed_by_forwarded_ip />`) in `docs/en/operations/quotas.md`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The failing test seems to be a borderline flaky-test, and looks like a job-timeout artifact.....rerunning the CI should fix this. |
|
Merged the latest I rebuilt locally on the merged base and verified the three affected tests against a fresh binary:
All 15 review threads are resolved. The only CI failures are |
…PV4_PREFIX_BITS `mask_address` in `QuotaCache.cpp` normalized an IPv4-mapped IPv6 address (such as `::ffff:192.0.2.10`) to native IPv4 before checking whether an IPv4 prefix was configured. This changed the quota key for existing `KEYED BY ip_address` / `KEYED BY forwarded_ip_address` quotas with no prefix bits: a client previously keyed as `::ffff:192.0.2.10` became `192.0.2.10`, breaking the contract that quotas without prefix bits behave as before. Now a mapped IPv4 address is treated as an IPv4 client governed solely by `IPV4_PREFIX_BITS`: it is normalized and masked only when `IPV4_PREFIX_BITS` is configured, and otherwise keeps its original representation. In particular, `IPV6_PREFIX_BITS` no longer collapses such addresses. Added a regression to `01600_quota_by_prefix_forwarded_ip` covering a forwarded `::ffff:1.2.3.4` with only `IPV6_PREFIX_BITS` set, asserting the key stays `::ffff:1.2.3.4`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Continued work on this PR:
Verified locally against a server built from this branch:
Pushed |
…rg::alter PR ClickHouse#106102 (merged 2026-06-05 17:43 UTC) refactored Iceberg::alter to declare last_version and compression_method outside an if/else that assigns them in both branches. Clang-tidy cppcoreguidelines-init-variables flags the bare declarations and Build (arm_tidy) is built with -warnings-as-errors, so the build fails for every PR whose CI ran on master after this commit. CIDB shows 30+ unrelated PRs hitting this in the last 2 days (e.g. ClickHouse#89360, ClickHouse#103540, ClickHouse#106120, ClickHouse#106386, ClickHouse#106404, ClickHouse#106522, ClickHouse#105102, ClickHouse#106590). Initialize both variables to safe defaults at declaration. They are unconditionally overwritten in both if and else branches before use, so behavior is unchanged. The new defaults are only relevant if a future code path skips both assignments, in which case last_version=0 and compression_method=CompressionMethod::None are sane no-op values (the same defaults the old structured-binding form would produce through default-construction of the destructured aggregate). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit d72cac5)
…f system.quotas Inserting the new `ipv4_prefix_bits` / `ipv6_prefix_bits` columns between `keys` and `durations` shifted the positional schema of `system.quotas`, so existing `SELECT *`, TSV/CSV, or tuple-unpacking consumers would read former `durations` / `apply_to_*` values from shifted positions. Move the two columns to the end, after `apply_to_except`, so named-column users get the new data while positional consumers keep reading the old columns from the same positions. Update the column accessors and fill logic in `fillData` accordingly, and adjust the `02117_show_create_table_system` reference and the `system.quotas` documentation to match the new order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LLVM Coverage Report
Changed lines: Changed C/C++ lines covered by tests: 233/244 (95.49%) | Lost baseline coverage (was covered on master, now uncovered in this PR): 2 line(s) · Uncovered code |
| namespace | ||
| { | ||
| void formatKeyType(const QuotaKeyType & key_type, WriteBuffer & ostr, const IAST::FormatSettings &) | ||
| void formatIpPrefixBits(const std::optional<MaskBits> & ipv4_prefix_bits, |
There was a problem hiding this comment.
@groeneai, only after I merge this PR, send another PR that will edit 'Ip' to 'IP' everywhere according to our code style.
keyed_by_ipquotas, add aipv4_prefix_bitsandipv6_prefix_bitsoptions. #79858keyed_by_ipor
Then, if q_2 is queried by, say a user with
forwarded_ip_address='1.2.3.4'first, then thequota_keygenerated :will give
1.2.3.0Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
IPV4_PREFIX_BITS and IPV6_PREFIX_BITSflags when Keying done byIP_ADDRESSorFORWARDED_IP_ADDRESSDocumentation entry for user-facing changes
Note
Medium Risk
Changes quota key derivation and SQL parsing/serialization for IP- and forwarded-IP-keyed quotas, which can alter how limits are shared/enforced across clients/subnets. Includes new system table columns and validation logic; regressions could affect quota correctness but scope is contained to quota handling.
Overview
Adds optional
IPV4_PREFIX_BITS/IPV6_PREFIX_BITSsettings to quotas keyed byip_addressorforwarded_ip_address, allowing quota buckets to be shared by masked subnet instead of full IP.Extends the
Quotaentity, SQL parser/formatter (CREATE/ALTER/SHOW CREATEincl.ON CLUSTER), users.xml quota parsing, andsystem.quotas(new nullable columns) to persist/expose these settings, and updatesQuotaCachekey calculation to apply masking (with a fast path and safe fallback for malformedX-Forwarded-For). Adds validation to reject prefix bits on non-IP key types and clears stale prefix bits when key type changes.Updates docs and tests, including new stateless coverage for subnet-masked quota keys and adjusted integration queries to avoid
SELECT *breakage from the newsystem.quotascolumns.Reviewed by Cursor Bugbot for commit b193898. Bugbot is set up for automated code reviews on this repo. Configure here.