Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

feat: Add rate limiting to identity search endpoint#6438

Open
1-23-smy wants to merge 12 commits intoFlagsmith:mainfrom
1-23-smy:feature/identity-search-rate-limiting
Open

feat: Add rate limiting to identity search endpoint#6438
1-23-smy wants to merge 12 commits intoFlagsmith:mainfrom
1-23-smy:feature/identity-search-rate-limiting

Conversation

@1-23-smy
Copy link

  • Backend: AddScopedRateThrottle toIdentityViewSet (30 req/min)
  • Frontend: Increase debounce from 500ms to 750ms
  • Config: AddIDENTITY_SEARCH_THROTTLE_RATE env variable
  • Tests: Add test for identity search throttling

This fixes aggressive API requests when searching for identities.


Thanks for submitting a PR! Please check the boxes below:

  • I have added information todocs/ if required so people know about the feature
  • I have filled in theChanges section below
  • I have filled in theHow did you test this code section below
  • I have used aConventional Commit title for this Pull Request

Changes

Backend Changes

  • api/environments/identities/views.py
    AddedScopedRateThrottle toIdentityViewSet with scopeidentity_search

  • api/app/settings/common.py
    Added configurableIDENTITY_SEARCH_THROTTLE_RATE environment variable (default:30/min)

Frontend Changes

  • frontend/common/useDebouncedSearch.ts
    Increased debounce time from 500ms to 750ms to reduce request frequency during typing

Configuration

The rate limit can be customized via environment variable:

IDENTITY_SEARCH_THROTTLE_RATE="30/min"# default

How did you test this code?

Unit Tests

  • Addedtest_identity_search_is_throttled in
    api/tests/unit/environments/identities/test_unit_identities_views.py

Manual Testing

  1. Built a local Docker image with the changes
  2. Made 35 rapid requests to the identity search endpoint:
foriin {1..35};do  curl -s -o /dev/null -w"Request$i: %{http_code}\n" \    -H"Authorization: Token$TOKEN" \"http://localhost:8009/api/v1/environments/$ENV_KEY/identities/?q=test"done
  1. Verified that requests after the 30/min threshold return429 Too Many Requests:
Request 33: 200Request 34: 429  ← Rate limit triggeredRequest 35: 429

- Backend: Add ScopedRateThrottle to IdentityViewSet (30 req/min)- Frontend: Increase debounce from 500ms to 750ms- Config: Add IDENTITY_SEARCH_THROTTLE_RATE env variable- Tests: Add test for identity search throttlingFixes aggressive API requests when searching for identities.
CopilotAI review requested due to automatic review settingsDecember 22, 2025 07:20
@1-23-smy1-23-smy requested review froma team ascode ownersDecember 22, 2025 07:20
@1-23-smy1-23-smy requested review fromkhvn26 andtalissoncosta and removed request fora teamDecember 22, 2025 07:20
@vercel
Copy link

vercelbot commentedDec 22, 2025

@smy-637q is attempting to deploy a commit to theFlagsmith Team onVercel.

A member of the Team first needs toauthorize it.

@github-actionsgithub-actionsbot added front-endIssue related to the React Front End Dashboard apiIssue related to the REST API labelsDec 22, 2025
Copy link
Contributor

CopilotAI 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 adds rate limiting to the identity search endpoint to prevent aggressive API requests. It includes backend throttling via Django REST Framework's ScopedRateThrottle, a configurable environment variable for the rate limit, frontend debounce increase, and a test to verify throttling behavior.

  • Backend throttling implementation withScopedRateThrottle at 30 requests/minute
  • Frontend debounce increase from 500ms to 750ms across all search components
  • Configurable rate limit viaIDENTITY_SEARCH_THROTTLE_RATE environment variable

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

FileDescription
api/environments/identities/views.pyAddsScopedRateThrottle withidentity_search scope toIdentityViewSet
api/app/settings/common.pyAddsIDENTITY_SEARCH_THROTTLE_RATE environment variable configuration (default: 30/min)
api/tests/unit/environments/identities/test_unit_identities_views.pyAdds test to verify identity search throttling behavior
frontend/common/useDebouncedSearch.tsIncreases debounce time from 500ms to 750ms for all search operations

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

def test_identity_search_is_throttled(
admin_client: APIClient,
environment: Environment,
settings,

Choose a reason for hiding this comment

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

The test is missing thereset_cache fixture parameter which is used in other throttle tests to ensure proper cache cleanup between tests. DRF's ScopedRateThrottle uses Django's cache backend to track request counts, and without cache clearing, throttle state could persist between tests causing flaky test failures.

Addreset_cache to the function parameters (see examples inapi/tests/integration/custom_auth/end_to_end/test_custom_auth_integration.py lines 486-491 and 527-533).

Suggested change
settings,
settings,
reset_cache,

Copilot uses AI. Check for mistakes.
Comment on lines 7 to 10
const [debounceTime, setDebounceTime] = useState(750)

useEffect(() => {
setDebounceTime(searchInput.length < 1 ? 0 :500)
setDebounceTime(searchInput.length < 1 ? 0 :750)

Choose a reason for hiding this comment

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

Increasing the debounce time from 500ms to 750ms will affect all components usinguseDebouncedSearch, not just identity search. This includes:

  • AuditLog.tsx
  • ConversionEventSelect.tsx
  • CreateSegment.tsx (segment search)
  • SegmentsPage.tsx
  • SplitTestPage.tsx
  • UserPage.tsx
  • UsersPage.tsx (identity search)
  • TableValueFilter.tsx

While this may be acceptable to reduce API calls globally, consider whether a 250ms increase is appropriate for all these use cases. If the intent is to only throttle identity search, consider creating a separate hook likeuseDebouncedIdentitySearch with the higher debounce time, or make the debounce time configurable via a parameter.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@Zaimwa9 are you able to chime in here - do you think this is something we need to be concerned about?

Copy link
Contributor

Choose a reason for hiding this comment

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

@1-23-smy thanks for the changes, could you explain the rationale behind the increase of the debounce? If this does not conflict with the extra throttling we'd better stick to 500. We aligned on 500 being the right UX time

cursor[bot]

This comment was marked as outdated.

smy-637qand others added2 commitsDecember 22, 2025 13:04
- Use get_throttles() method to conditionally apply throttle only to 'list' action- Update test to use mocker.patch and reset_cache fixture for proper cleanup- Follow existing patterns from FFAdminUserViewSet and OrganisationViewSet
@vercel
Copy link

vercelbot commentedDec 31, 2025
edited
Loading

The latest updates on your projects. Learn more aboutVercel for GitHub.

ProjectDeploymentReviewUpdated (UTC)
docsReadyReadyPreview,CommentDec 31, 2025 11:09am
2 Skipped Deployments
ProjectDeploymentReviewUpdated (UTC)
flagsmith-frontend-previewIgnoredIgnoredPreviewDec 31, 2025 11:09am
flagsmith-frontend-stagingIgnoredIgnoredPreviewDec 31, 2025 11:09am

@matthewelwell
Copy link
Contributor

@1-23-smy could you check the unit test failures here please?

@1-23-smy
Copy link
Author

1-23-smy commentedDec 31, 2025
edited
Loading

matthewelwell Sure, I will look into it

smy-637qand others added2 commitsDecember 31, 2025 16:49
The test.py settings file overrides DEFAULT_THROTTLE_RATES from common.py,so identity_search scope was missing in the test environment.
@1-23-smy
Copy link
Author

@1-23-smy could you check the unit test failures here please?

@matthewelwell Could you review once ?

@matthewelwell
Copy link
Contributor

@1-23-smy could you check the unit test failures here please?

@matthewelwell Could you review once ?

Thanks - I'll take a look, but you do have some conflicts that also need resolving please.

@codecov
Copy link

codecovbot commentedJan 18, 2026
edited
Loading

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.09%. Comparing base (9468354) to head (ba7ab9c).

Additional details and impacted files
@@           Coverage Diff           @@##             main    #6438   +/-   ##=======================================  Coverage   98.09%   98.09%           =======================================  Files        1293     1293             Lines       46610    46624   +14     =======================================+ Hits        45722    45736   +14  Misses        888      888

☔ 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.
  • 📦JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@1-23-smy
Copy link
Author

@1-23-smy could you check the unit test failures here please?

@matthewelwell Could you review once ?

Thanks - I'll take a look, but you do have some conflicts that also need resolving please.

@matthewelwell I have fixed the conflicts. Could you please review once ?

Copy link
Contributor

@Zaimwa9Zaimwa9 left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution 👍 . 2 comments, the most important one would be to stick with the previous debounce time if you don't mind

Comment on lines 7 to 10
const [debounceTime, setDebounceTime] = useState(750)

useEffect(() => {
setDebounceTime(searchInput.length < 1 ? 0 :500)
setDebounceTime(searchInput.length < 1 ? 0 :750)
Copy link
Contributor

Choose a reason for hiding this comment

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

@1-23-smy thanks for the changes, could you explain the rationale behind the increase of the debounce? If this does not conflict with the extra throttling we'd better stick to 500. We aligned on 500 being the right UX time

"""
if getattr(self, "action", None) == "list":
return [ScopedRateThrottle()]
return []
Copy link
Contributor

Choose a reason for hiding this comment

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

Even though the impact is minimal, i would suggest to return the global default throttlesuper().get_throttles here (defaulting toDEFAULT_THROTTLE_CLASSES). Similarly to what we didhere

Copy link
Author

Choose a reason for hiding this comment

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

@Zaimwa9

  • I have reverted debounce from 750ms back to 500ms (agreed UX time).
  • Updated get_throttles() to return super().get_throttles() for non-list actions.

Zaimwa9 reacted with thumbs up emoji
Copy link
Contributor

Choose a reason for hiding this comment

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

thanks a lot! Just some conflicts to fix and we should be good to enable the workflows and get it over the line. Again, thanks for the contribution, we appreciate!

1-23-smy reacted with thumbs up emoji
Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the review! the conflicts should now be resolved.

Let me know if there's anything else you'd like me to adjust!

- Revert frontend debounce from 750ms back to 500ms (agreed UX time)- Return super().get_throttles() for non-list actions so global default throttle classes still apply
Zaimwa9
Zaimwa9 previously approved these changesJan 20, 2026
Copy link
Contributor

@Zaimwa9Zaimwa9 left a comment

Choose a reason for hiding this comment

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

Looking good to me, I allowed myself to fix some loss - probably while fixing conflicts. Thanks for the contribution!

1-23-smy reacted with thumbs up emoji
Copy link
Author

@1-23-smy1-23-smy left a comment
edited
Loading

Choose a reason for hiding this comment

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

Thank You.

Copy link
Contributor

CopilotAI 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

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.


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

Apply identity_search throttle only to list (search) requests.
For other actions, return the global default throttle classes.
"""
if getattr(self, "action", None) == "list":
Copy link

CopilotAIFeb 5, 2026

Choose a reason for hiding this comment

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

The comment states "Apply identity_search throttle only to list (search) requests" but the implementation applies throttling to ALL list requests, regardless of whether they include a search query parameter. The current implementation throttles both/identities/ and/identities/?q=test equally.

If the intent is to only throttle search requests (when the 'q' parameter is present), the condition should check for the presence of the query parameter. Otherwise, if throttling all list requests is the intended behavior, update the comment to accurately reflect this.

Suggested change
ifgetattr(self,"action",None)=="list":
ifgetattr(self,"action",None)=="list"and"q"inself.request.query_params:

Copilot uses AI. Check for mistakes.
Comment on lines +308 to +331
def test_identity_search_is_throttled(
admin_client: APIClient,
environment: Environment,
reset_cache: None,
mocker: MockerFixture,
) -> None:
# Given - mock the throttle rate to be restrictive for testing
mocker.patch(
"rest_framework.throttling.ScopedRateThrottle.get_rate", return_value="1/minute"
)
base_url = reverse(
"api-v1:environments:environment-identities-list",
args=[environment.api_key],
)
url = f"{base_url}?q=test"

# When - make 2 requests in quick succession
response1 = admin_client.get(url)
response2 = admin_client.get(url)

# Then - first should succeed, second should be throttled
assert response1.status_code == status.HTTP_200_OK
assert response2.status_code == status.HTTP_429_TOO_MANY_REQUESTS

Copy link

CopilotAIFeb 5, 2026

Choose a reason for hiding this comment

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

The test only verifies that the list action with a search query is throttled, but it doesn't verify that other actions (create, retrieve, update, delete) are NOT throttled. Following the pattern from api/tests/integration/custom_auth/end_to_end/test_custom_auth_integration.py:563-575 (test_get_user_is_not_throttled), consider adding a test to ensure other actions on the IdentityViewSet are not affected by the throttle.

Copilot uses AI. Check for mistakes.
SIGNUP_THROTTLE_RATE = env("SIGNUP_THROTTLE_RATE", "10000/min")
USER_THROTTLE_RATE = env("USER_THROTTLE_RATE", "500/min")
MASTER_API_KEY_THROTTLE_RATE = env("MASTER_API_KEY_THROTTLE_RATE", USER_THROTTLE_RATE)
IDENTITY_SEARCH_THROTTLE_RATE = env("IDENTITY_SEARCH_THROTTLE_RATE", "30/min")
Copy link

CopilotAIFeb 5, 2026

Choose a reason for hiding this comment

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

The PR description mentions "Frontend Changes" that increase debounce from 500ms to 750ms infrontend/common/useDebouncedSearch.ts, but this file is not included in the current changes. Based on previous review feedback, there were concerns about this change affecting all components using the hook, and it was suggested to stick to 500ms if there's no conflict with the backend throttling. Either include the frontend changes in this PR or remove the mention from the PR description.

Copilot uses AI. Check for mistakes.
Copy link

@cursorcursorbot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in theCursor dashboard.

USER_THROTTLE_RATE = env("USER_THROTTLE_RATE", default=None)
MASTER_API_KEY_THROTTLE_RATE = env("MASTER_API_KEY_THROTTLE_RATE", default=None)
USER_THROTTLE_RATE = env("USER_THROTTLE_RATE", "500/min")
MASTER_API_KEY_THROTTLE_RATE = env("MASTER_API_KEY_THROTTLE_RATE", USER_THROTTLE_RATE)
Copy link

Choose a reason for hiding this comment

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

Undocumented breaking change to global throttle rate defaults

Medium Severity

The PR changesUSER_THROTTLE_RATE andMASTER_API_KEY_THROTTLE_RATE defaults fromNone (no throttling) to"500/min", but this behavioral change isn't mentioned in the PR description. The stated scope is only adding rate limiting to the identity search endpoint. Existing deployments that hadDEFAULT_THROTTLE_CLASSES configured but relied on theNone default for these rates would suddenly have 500/min rate limiting applied to all admin API requests. If intentional, this change warrants explicit documentation in the PR description.

Fix in Cursor Fix in Web

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

Reviewers

Copilot code reviewCopilotCopilot left review comments

@cursorcursor[bot]cursor[bot] left review comments

@Zaimwa9Zaimwa9Zaimwa9 left review comments

@talissoncostatalissoncostaAwaiting requested review from talissoncostatalissoncosta was automatically assigned from Flagsmith/flagsmith-front-end

@khvn26khvn26Awaiting requested review from khvn26khvn26 is a code owner automatically assigned from Flagsmith/flagsmith-back-end

@matthewelwellmatthewelwellAwaiting requested review from matthewelwell

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

apiIssue related to the REST APIfront-endIssue related to the React Front End Dashboard

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

4 participants

@1-23-smy@matthewelwell@Zaimwa9@smy-637q

[8]ページ先頭

©2009-2026 Movatter.jp