Skip to content

Conversation

@tisnik
Copy link
Contributor

@tisnik tisnik commented Oct 20, 2025

Description

LCORE-739: quota limiter stub code

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement

Related Tickets & Documents

  • Related Issue #LCORE-739

Summary by CodeRabbit

  • New Features
    • Added a quota management system to track, monitor, and enforce token limits across resources for fair usage.
    • Improved user-facing error messages for quota exceed situations, clearly showing subject and token availability/needs.
    • Introduced an extensible limiter interface and factory scaffold to support multiple quota limiter implementations going forward.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 20, 2025

Walkthrough

Adds three quota-management components: a QuotaExceedError exception, an abstract QuotaLimiter interface, and a QuotaLimiterFactory that returns quota limiter instances from configuration.

Changes

Cohort / File(s) Summary
Quota exception handling
src/quota/quota_exceed_error.py
New QuotaExceedError exception class. Accepts subject_id, subject_type, available, and optional needed; stores these attributes and builds a user-facing message for user/cluster/unknown subject types.
Abstract quota management interface
src/quota/quota_limiter.py
New abstract base class QuotaLimiter defining methods: available_quota, revoke_quota, increase_quota, ensure_available_quota, consume_tokens, __init__, _initialize_tables, plus connect and connected helpers and module logger setup.
Quota limiter factory
src/quota/quota_limiter_factory.py
New QuotaLimiterFactory with static quota_limiters(config: QuotaHandlersConfiguration) -> list[QuotaLimiter]. Currently handles missing config and returns an empty list (placeholder).

Sequence Diagram(s)

sequenceDiagram
    participant Consumer as Client/Request
    participant Factory as QuotaLimiterFactory
    participant Limiter as QuotaLimiter (impl)
    participant Error as QuotaExceedError

    Note over Consumer,Factory: Initialization flow
    Consumer->>Factory: quota_limiters(config)
    Factory-->>Consumer: [list of QuotaLimiter] (may be empty)

    Note over Consumer,Limiter: Runtime token-check flow
    Consumer->>Limiter: available_quota(subject_id)
    alt not enough
        Limiter-->>Consumer: raise QuotaExceedError
        Consumer->>Error: handle/report
    else sufficient
        Consumer->>Limiter: consume_tokens(input, output, subject_id)
        Limiter-->>Consumer: success/ack
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 I bounded in code with a hop and a cheer,
New limiters sprout so tokens appear,
A factory sows lists, an error warns loud,
Quietly managing the token-filled crowd,
Hop on—quota's in hand, let's steer! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "LCORE-739: quota limiter stub code" directly and accurately describes the main changes in the changeset. The three new files introduced (QuotaExceedError exception class, QuotaLimiter abstract base class, and QuotaLimiterFactory) collectively form the stub code foundation for a quota limiter system. The title is concise, specific, and clearly communicates the primary change without unnecessary noise or vague terminology. A teammate reviewing the commit history would immediately understand that this PR introduces the foundational skeleton code for quota limiter functionality.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 445d2e0 and de233f8.

📒 Files selected for processing (3)
  • src/quota/quota_exceed_error.py (1 hunks)
  • src/quota/quota_limiter.py (1 hunks)
  • src/quota/quota_limiter_factory.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/quota/quota_limiter.py
  • src/quota/quota_exceed_error.py
  • src/quota/quota_limiter_factory.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-pr
  • GitHub Check: e2e_tests (ci)
  • GitHub Check: e2e_tests (azure)

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
src/quota/quota_exceed_error.py (3)

1-1: Improve docstring precision and eliminate duplication.

The module and class docstrings are identical and both reference only "user" when the implementation supports multiple subject types (user, cluster, unknown).

Apply this diff to improve clarity:

-"""Any exception that can occur when user does not have enough tokens available."""
+"""Exception for quota exhaustion scenarios across different subject types."""
-    """Any exception that can occur when user does not have enough tokens available."""
+    """Raised when a subject (user, cluster, etc.) has insufficient tokens available."""

Also applies to: 5-5


7-10: Enhance constructor docstring per coding guidelines.

The constructor docstring should follow Google Python style and document all parameters.

As per coding guidelines, apply this diff:

-    def __init__(
-        self, subject_id: str, subject_type: str, available: int, needed: int = 0
-    ) -> None:
-        """Construct exception object."""
+    def __init__(
+        self, subject_id: str, subject_type: str, available: int, needed: int = 0
+    ) -> None:
+        """Initialize quota exceeded exception with subject and token details.
+
+        Args:
+            subject_id: Identifier of the subject (user ID, cluster name, etc.).
+            subject_type: Type code ('u' for user, 'c' for cluster).
+            available: Number of tokens currently available.
+            needed: Number of tokens required (default 0 for general exhaustion).
+        """

33-36: Consider storing subject_type as an attribute.

The subject_type parameter is used to format the message but isn't stored as an instance attribute. Storing it would allow exception handlers to programmatically determine the subject type without parsing the message.

Apply this diff:

 # custom attributes
 self.subject_id = subject_id
+self.subject_type = subject_type
 self.available = available
 self.needed = needed
src/quota/quota_limiter.py (2)

35-41: Reconsider abstract __init__ design pattern.

Marking __init__ as abstract is unusual and can complicate subclass implementation. Subclasses must call super().__init__() but cannot since the abstract method has no implementation. Additionally, having an abstract private method (_initialize_tables) is unconventional.

Consider this alternative pattern:

-@abstractmethod
-def __init__(self) -> None:
-    """Initialize connection config."""
+def __init__(self) -> None:
+    """Initialize connection config and tables."""
+    self._initialize_tables()
 
 @abstractmethod
 def _initialize_tables(self) -> None:
     """Initialize tables and indexes."""

This allows subclasses to override _initialize_tables while providing a standard initialization flow.


43-49: Clarify stub implementations for connection methods.

The connect() method has no body (implicit pass) but isn't abstract, and connected() unconditionally returns True, which could be misleading in a stub implementation.

Consider making connect() abstract or adding a pass statement for clarity:

 # pylint: disable=W0201
+@abstractmethod
 def connect(self) -> None:
     """Initialize connection to database."""

And update connected() to reflect stub status:

+@abstractmethod
 def connected(self) -> bool:
     """Check if connection to cache is alive."""
-    return True

Alternatively, if these should remain concrete with default implementations, add explicit pass/return statements with clarifying comments:

 def connect(self) -> None:
     """Initialize connection to database."""
+    pass  # Stub implementation - override in subclasses
 
 def connected(self) -> bool:
     """Check if connection to cache is alive."""
-    return True
+    return True  # Stub implementation - override in subclasses
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf14f9e and 445d2e0.

📒 Files selected for processing (3)
  • src/quota/quota_exceed_error.py (1 hunks)
  • src/quota/quota_limiter.py (1 hunks)
  • src/quota/quota_limiter_factory.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Use absolute imports for internal modules (e.g., from auth import get_auth_dependency)

Files:

  • src/quota/quota_exceed_error.py
  • src/quota/quota_limiter_factory.py
  • src/quota/quota_limiter.py
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: All modules start with descriptive module-level docstrings explaining purpose
Use logger = logging.getLogger(name) for module logging after import logging
Define type aliases at module level for clarity
All functions require docstrings with brief descriptions
Provide complete type annotations for all function parameters and return types
Use typing_extensions.Self in model validators where appropriate
Use modern union syntax (str | int) and Optional[T] or T | None consistently
Function names use snake_case with descriptive, action-oriented prefixes (get_, validate_, check_)
Avoid in-place parameter modification; return new data structures instead of mutating arguments
Use appropriate logging levels: debug, info, warning, error with clear messages
All classes require descriptive docstrings explaining purpose
Class names use PascalCase with conventional suffixes (Configuration, Error/Exception, Resolver, Interface)
Abstract base classes should use abc.ABC and @AbstractMethod for interfaces
Provide complete type annotations for all class attributes
Follow Google Python docstring style for modules, classes, and functions, including Args, Returns, Raises, Attributes sections as needed

Files:

  • src/quota/quota_exceed_error.py
  • src/quota/quota_limiter_factory.py
  • src/quota/quota_limiter.py
🧬 Code graph analysis (1)
src/quota/quota_limiter_factory.py (2)
src/models/config.py (2)
  • config (140-146)
  • QuotaHandlersConfiguration (584-593)
src/quota/quota_limiter.py (1)
  • QuotaLimiter (10-49)
🪛 GitHub Actions: Python linter
src/quota/quota_exceed_error.py

[error] 24-24: Pylint: Line too long (119/100).


[error] 28-28: Pylint: Line too long (130/100).

src/quota/quota_limiter_factory.py

[error] 17-17: W0613 Unused argument 'config' (unused-argument).


[error] 13-13: R0903 Too few public methods (1/2).


[error] 7-7: W0611 Unused USER_QUOTA_LIMITER imported from constants (unused-import).


[error] 7-7: W0611 Unused CLUSTER_QUOTA_LIMITER imported from constants (unused-import).

🪛 GitHub Actions: Ruff
src/quota/quota_limiter_factory.py

[error] 7-7: F401: 'USER_QUOTA_LIMITER' imported but unused. Remove unused import.


[error] 7-7: F401: 'CLUSTER_QUOTA_LIMITER' imported but unused. Remove unused import.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-pr
  • GitHub Check: e2e_tests (ci)
  • GitHub Check: e2e_tests (azure)
🔇 Additional comments (3)
src/quota/quota_limiter.py (2)

1-8: LGTM!

Module setup follows coding guidelines: descriptive docstring, proper imports, and logger configuration with __name__.


29-33: LGTM!

The consume_tokens method signature is appropriate for tracking token consumption with proper type annotations.

src/quota/quota_limiter_factory.py (1)

13-14: LGTM!

The single-method factory class is an acceptable pattern for stub code. The "too few public methods" warning can be safely ignored for factory and utility classes.

else:
match subject_type:
case "u":
message = f"User {subject_id} has {available} tokens, but {needed} tokens are needed" # noqa: E501
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix line length violations flagged by pipeline.

Lines 24 and 28 exceed the 100-character limit despite having # noqa: E501 comments, indicating the linter is still flagging them.

Apply this diff to resolve the violations:

-                    message = f"User {subject_id} has {available} tokens, but {needed} tokens are needed"  # noqa: E501
+                    message = (
+                        f"User {subject_id} has {available} tokens, "
+                        f"but {needed} tokens are needed"
+                    )
-                    message = f"Unknown subject {subject_id} has {available} tokens, but {needed} tokens are needed"  # noqa: E501
+                    message = (
+                        f"Unknown subject {subject_id} has {available} tokens, "
+                        f"but {needed} tokens are needed"
+                    )

Also applies to: 28-28

🧰 Tools
🪛 GitHub Actions: Python linter

[error] 24-24: Pylint: Line too long (119/100).

🤖 Prompt for AI Agents
In src/quota/quota_exceed_error.py around lines 24 and 28, the long f-strings
exceed the 100-character limit and the trailing "# noqa: E501" is still being
flagged; shorten those log/message lines by breaking the message into smaller
parts or using intermediate variables so each physical line is under 100 chars,
remove the "# noqa: E501" comments, and rebuild the full message via
concatenation or format across multiple lines to preserve the same runtime text.

Comment on lines 16 to 24
@staticmethod
def quota_limiters(config: QuotaHandlersConfiguration) -> list[QuotaLimiter]:
"""Create instances of quota limiters based on loaded configuration.

Returns:
List of instances of 'QuotaLimiter',
"""
limiters: list[QuotaLimiter] = []
return limiters
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document stub implementation and address unused parameter warning.

The config parameter is unused, triggering a pipeline error. For stub code that will be implemented later, consider adding a clarifying comment or suppressing the warning.

Apply this diff to suppress the warning and document the stub:

 @staticmethod
 def quota_limiters(config: QuotaHandlersConfiguration) -> list[QuotaLimiter]:
     """Create instances of quota limiters based on loaded configuration.
+    
+    Note: Stub implementation - will instantiate concrete limiters based on config.
 
     Returns:
         List of instances of 'QuotaLimiter',
     """
+    # pylint: disable=unused-argument
     limiters: list[QuotaLimiter] = []
+    # TODO: Instantiate limiters based on config.limiters
     return limiters
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@staticmethod
def quota_limiters(config: QuotaHandlersConfiguration) -> list[QuotaLimiter]:
"""Create instances of quota limiters based on loaded configuration.
Returns:
List of instances of 'QuotaLimiter',
"""
limiters: list[QuotaLimiter] = []
return limiters
@staticmethod
def quota_limiters(config: QuotaHandlersConfiguration) -> list[QuotaLimiter]:
"""Create instances of quota limiters based on loaded configuration.
Note: Stub implementation - will instantiate concrete limiters based on config.
Returns:
List of instances of 'QuotaLimiter',
"""
# pylint: disable=unused-argument
limiters: list[QuotaLimiter] = []
# TODO: Instantiate limiters based on config.limiters
return limiters
🧰 Tools
🪛 GitHub Actions: Python linter

[error] 17-17: W0613 Unused argument 'config' (unused-argument).

🤖 Prompt for AI Agents
In src/quota/quota_limiter_factory.py around lines 16 to 24, the parameter
`config` is unused which triggers a pipeline/linter error; mark this function as
a documented stub and silence the unused-parameter warning by adding a short
TODO comment and a no-op reference to the parameter (e.g. assign it to `_` or
call `cast(None, config)`) so the linter sees it as used; keep the current
return of an empty list and include a one-line TODO explaining this will be
implemented later.

Comment on lines +18 to +19
def revoke_quota(self) -> None:
"""Revoke quota for given user."""
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Align docstrings with method signatures.

Both revoke_quota and increase_quota docstrings mention "for given user" but neither method accepts a user/subject parameter. Either add the parameter or update the docstrings to match the actual signatures.

If these methods should operate on a specific subject, apply this diff:

 @abstractmethod
-def revoke_quota(self) -> None:
-    """Revoke quota for given user."""
+def revoke_quota(self, subject_id: str = "") -> None:
+    """Revoke quota for given subject."""
 
 @abstractmethod
-def increase_quota(self) -> None:
-    """Increase quota for given user."""
+def increase_quota(self, subject_id: str = "") -> None:
+    """Increase quota for given subject."""

Otherwise, update docstrings to remove the subject reference:

-    """Revoke quota for given user."""
+    """Revoke quota."""
-    """Increase quota for given user."""
+    """Increase quota."""

Also applies to: 22-23

🤖 Prompt for AI Agents
In src/quota/quota_limiter.py around lines 18-19 and 22-23, the docstrings for
revoke_quota and increase_quota incorrectly say "for given user" while the
method signatures take no user/subject; update both docstrings to remove the
reference to a user and describe the actual behavior (e.g., "Revoke quota." and
"Increase quota." or a brief phrase describing the scope), or alternatively if
the methods should accept a subject, add the subject parameter to the signatures
and update call sites and docstrings accordingly—pick one approach and make the
code and docstrings consistent.

@tisnik tisnik merged commit 1ef6c87 into lightspeed-core:main Oct 20, 2025
18 of 20 checks passed
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.

1 participant