Skip to content

docs(adr): bounded attribute value responses#3747

Open
eugenioenko wants to merge 3 commits into
mainfrom
docs/adr-attributes-v2-bounded-values
Open

docs(adr): bounded attribute value responses#3747
eugenioenko wants to merge 3 commits into
mainfrom
docs/adr-attributes-v2-bounded-values

Conversation

@eugenioenko

@eugenioenko eugenioenko commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a proposed ADR evaluating five options for addressing unbounded JSON_AGG on attribute values in the Attributes Service
  • Rejects options 1 (modify v1) and 4 (application-layer cap) as breaking changes
  • Remaining options: v2 with inline sub-pagination, v2 with dedicated ListAttributeValues RPC, or do nothing

Related

Summary by CodeRabbit

  • Documentation
    • Added an architecture decision record proposing paginated retrieval for attribute values.
    • Documents a future approach that keeps attribute listing responses bounded while preserving compatibility with existing consumers.
    • Compares alternative designs and outlines considerations for performance, pagination, authorization, and platform alignment.

eugenioenko and others added 2 commits July 10, 2026 10:49
Adds a proposed ADR evaluating five options for addressing unbounded
JSON_AGG on attribute values in the Attributes Service (issue #3744).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Eugene Yakhnenko <eugene.yakhnenko@virtru.com>
Reduce verbosity, add rejected alternatives section, clarify that
options 1 and 4 are breaking changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Eugene Yakhnenko <eugene.yakhnenko@virtru.com>
@eugenioenko
eugenioenko requested a review from a team as a code owner July 10, 2026 18:05
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an ADR proposing bounded Attributes Service v2 responses, with values omitted from attribute listings and single-attribute fetches and retrieved through a paginated ListAttributeValues RPC.

Changes

Attributes Service v2 decision

Layer / File(s) Summary
Bounded value response decision
adr/decisions/2026-07-10-attributes-v2-bounded-value-responses.md
Documents the unbounded aggregation problem, defines the v2 response and pagination direction, compares alternatives, and records validation placeholders and references.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

  • Issue 3744 — Directly covers value-free attribute responses and reintroducing paginated ListAttributeValues.

Poem

A bunny saw values heap and grow,
So bounded paths began to flow.
“List them page by page,”
Said the rabbit sage,
“And keep each response in tow!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the ADR about bounding attribute value responses and is concise and specific.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/adr-attributes-v2-bounded-values

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces an Architecture Decision Record (ADR) to resolve performance bottlenecks in the Attributes Service. The current implementation relies on unbounded JSON aggregation, which negatively impacts database performance and response latency. The document outlines various strategies to implement pagination while maintaining backward compatibility and adhering to established platform design patterns.

Highlights

  • ADR Proposal: Introduced a new Architecture Decision Record (ADR) to address performance issues caused by unbounded JSON_AGG operations on attribute values.
  • Option Evaluation: Evaluated five distinct strategies for paginating attribute values, ultimately rejecting modifications to v1 RPCs and application-layer caps due to breaking change concerns.
  • Recommended Path: Proposed a v2 approach featuring a dedicated ListAttributeValues RPC to decouple attribute metadata from value retrieval, aligning with existing platform patterns.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


The query grows wide and deep, / The database starts to weep. / We seek a new way, / To keep lag at bay, / And let the pagination sleep.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request proposes an Architecture Decision Record (ADR) to address performance issues caused by unbounded attribute value responses in the Attributes Service, recommending a v2 API with a dedicated, paginated ListAttributeValues RPC. The review feedback suggests clarifying that ListAttributeValues was deprecated rather than completely removed in PR #3108, and highlights the SQL complexity of paginating across multiple attribute IDs in a single query as an additional drawback of the chosen option.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.


`ListAttributes`, `GetAttribute`, and `ListAttributesByFqns` return all attribute values inline via unbounded `JSON_AGG`. High-cardinality attributes cause Postgres memory pressure, oversized gRPC responses, and high latency.

`ListAttributeValues` was removed in [PR #3108](https://github.com/opentdf/platform/pull/3108), leaving no paginated way to fetch values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The ListAttributeValues RPC is still defined in service/policy/attributes/attributes.proto (line 543) but is marked as deprecated. To be precise, it was deprecated rather than completely removed from the API definition. Clarifying this distinction helps avoid confusion for readers looking at the current proto files.

Suggested change
`ListAttributeValues` was removed in [PR #3108](https://github.com/opentdf/platform/pull/3108), leaving no paginated way to fetch values.
`ListAttributeValues` was deprecated in [PR #3108](https://github.com/opentdf/platform/pull/3108) (though the RPC definition remains in the v1 proto), leaving no active paginated way to fetch values.

* 🟩 **Good**, multi-attribute fetch in one paginated call avoids N+1
* 🟩 **Good**, v2 versioning follows authorization v2 pattern, non-breaking
* 🟨 **Neutral**, consumers need two calls for attribute + values
* 🟥 **Bad**, new SQL queries partially duplicate existing ones (minus values join)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Paginating across multiple attribute IDs in a single query can introduce significant SQL complexity and potential performance overhead, especially when dealing with high-cardinality attributes and sorting/filtering. It would be beneficial to list this as a potential drawback or complexity to address during implementation.

Suggested change
* 🟥 **Bad**, new SQL queries partially duplicate existing ones (minus values join)
* 🟥 **Bad**, new SQL queries partially duplicate existing ones (minus values join)\n* 🟥 **Bad**, pagination logic across multiple `attribute_ids` in a single query is complex to implement efficiently

## More Information

* [Issue #3744](https://github.com/opentdf/platform/issues/3744)
* [PR #3108](https://github.com/opentdf/platform/pull/3108) removed `ListAttributeValues`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Update this reference to reflect that the RPC was deprecated rather than completely removed.

Suggested change
* [PR #3108](https://github.com/opentdf/platform/pull/3108) removed `ListAttributeValues`
* [PR #3108](https://github.com/opentdf/platform/pull/3108) deprecated `ListAttributeValues`

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adr/decisions/2026-07-10-attributes-v2-bounded-value-responses.md`:
- Around line 21-35: Revise the ADR’s “Decision Drivers” and related decision
sections to scope elimination of unbounded JSON_AGG explicitly to v2, or
document how unchanged v1 RPCs are mitigated. For the v1 compatibility
discussion and Option 3, state the residual risk and define migration,
deprecation, or operational guardrails for v1 consumers, ensuring the selected
option’s scope matches its stated driver.
- Around line 41-48: Revise the Option 1 assessment to distinguish request
compatibility from response semantics: state that omitting values_pagination
preserves existing callers, while opting in introduces a different response
shape requiring explicit pagination metadata and semantics. Remove the
contradictory “breaking change disguised as an optional parameter” wording and
update the rejection rationale accordingly.
- Around line 69-76: Revise “Option 4: Application-layer cap” to accurately
distinguish database-side and application-side capping: remove the claim that
LIMIT can be used directly inside JSON_AGG, describe pre-aggregation subqueries,
LATERAL joins, or window filtering as required for database-side limits, and
separately describe truncating or erroring after fetching the full result.
Update the pros and cons to reflect each approach before evaluating them.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: de1b98e8-d2c4-4646-ae5e-2a9fc8914abb

📥 Commits

Reviewing files that changed from the base of the PR and between 3f32839 and a890af7.

📒 Files selected for processing (1)
  • adr/decisions/2026-07-10-attributes-v2-bounded-value-responses.md

Comment on lines +21 to +35
## Decision Drivers

* Eliminate unbounded `JSON_AGG` on attribute values
* Provide paginated value retrieval
* No breaking changes to v1 consumers
* Follow existing platform patterns (`ListRegisteredResourceValues`, authorization v2)
* Support multi-attribute value fetching in a single call

## Considered Options

1. **Modify v1 RPCs**: add `values_pagination` to existing requests
2. **v2 with inline sub-pagination**: v2 `GetAttribute` returns paginated values inline, `ListAttributes` strips values
3. **v2 with dedicated ListAttributeValues**: v2 strips values from all attribute RPCs, new `ListAttributeValues` RPC with pagination
4. **Application-layer cap**: `LIMIT` inside `JSON_AGG`, no API changes
5. **Do nothing**: accept current behavior

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Scope the “eliminate unbounded JSON_AGG” driver to the supported version.

Option 3 only bounds v2 responses; unchanged v1 RPCs remain unbounded. The ADR should explicitly accept that residual risk and define migration, deprecation, or operational guardrails—or change the driver to “eliminate unbounded aggregation in v2.” As written, the proposed decision does not satisfy its primary driver for existing v1 consumers. (github.com)

Also applies to: 58-68

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adr/decisions/2026-07-10-attributes-v2-bounded-value-responses.md` around
lines 21 - 35, Revise the ADR’s “Decision Drivers” and related decision sections
to scope elimination of unbounded JSON_AGG explicitly to v2, or document how
unchanged v1 RPCs are mitigated. For the v1 compatibility discussion and Option
3, state the residual risk and define migration, deprecation, or operational
guardrails for v1 consumers, ensuring the selected option’s scope matches its
stated driver.

Comment on lines +41 to +48
Add optional `values_pagination` to v1 requests. Default returns all values for backward compatibility.

* 🟩 **Good**, no new service version or RPCs
* 🟩 **Good**, existing consumers unaffected
* 🟥 **Bad**, default path still unbounded
* 🟥 **Bad**, per-attribute pagination cursors inside a list response is complex
* 🟥 **Bad**, Postgres still joins and aggregates all values unless SQL is restructured
* 🟥 **Bad**, using the optional field changes the response shape, making it a breaking change disguised as an optional parameter

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Separate backward compatibility from opt-in response semantics.

Line [41] says existing consumers remain unaffected when the field is omitted, but Line [48] calls the opt-in behavior a breaking change. Adding an optional request field is compatible for existing callers; the new mode may still require clear documentation of pagination metadata and response semantics. The current wording weakens the rejection rationale for Option 1.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adr/decisions/2026-07-10-attributes-v2-bounded-value-responses.md` around
lines 41 - 48, Revise the Option 1 assessment to distinguish request
compatibility from response semantics: state that omitting values_pagination
preserves existing callers, while opting in introduces a different response
shape requiring explicit pagination metadata and semantics. Remove the
contradictory “breaking change disguised as an optional parameter” wording and
update the rejection rationale accordingly.

Comment thread adr/decisions/2026-07-10-attributes-v2-bounded-value-responses.md Outdated
@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 218.022651ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 122.023365ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 746.765133ms
Throughput 133.91 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 44.802189009s
Average Latency 446.42443ms
Throughput 111.60 requests/second

Add Decision Outcome section, cap repeated attribute_ids input,
note ListAttributesByFqns replaced by ListAttributes with FQN filter,
trim rejected alternatives, reclassify two-call cost as Bad, fix
Option 4 SQL analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Eugene Yakhnenko <eugene.yakhnenko@virtru.com>
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 201.06763ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 99.121843ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 447.243271ms
Throughput 223.59 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 44.145681616s
Average Latency 440.0383ms
Throughput 113.26 requests/second

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • examples
  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

* 🟩 **Good**, multi-attribute fetch in one paginated call avoids N+1
* 🟩 **Good**, v2 versioning follows authorization v2 pattern, non-breaking
* 🟥 **Bad**, consumers need two calls for attribute + values
* 🟥 **Bad**, new SQL queries partially duplicate existing ones (minus values join)

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.

Suggested change
* 🟥 **Bad**, new SQL queries partially duplicate existing ones (minus values join)
* 🟥 **Bad**, new SQL queries partially duplicate existing ones (minus values join)
* 🟥 **Bad**, offset/limit pagination means deep offsets get expensive

@eugenioenko eugenioenko Jul 13, 2026

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.

Thank you for taking a look! That's technically 100% correct.

Adding it here though would unfairly weight against this option when Options 1 and 2 would have the same issue.
offset/limit to a deep value is an inherent problem of pagination that is applied in the rest of list endpoints as well.

We could mitigate it by providing next cursor pagination but that should be done for all pagination lists and sounds like a separate (and very valid) ADR

@pflynn-virtru pflynn-virtru left a comment

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.

Decision 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants