Skip to content

⚡️ Speed up function get_optional_user_store_api_key by 2,105% in PR #10702 (pluggable-auth-service)#11624

Closed
codeflash-ai[bot] wants to merge 175 commits into
mainfrom
codeflash/optimize-pr10702-2026-02-06T15.27.02
Closed

⚡️ Speed up function get_optional_user_store_api_key by 2,105% in PR #10702 (pluggable-auth-service)#11624
codeflash-ai[bot] wants to merge 175 commits into
mainfrom
codeflash/optimize-pr10702-2026-02-06T15.27.02

Conversation

@codeflash-ai
Copy link
Copy Markdown
Contributor

@codeflash-ai codeflash-ai Bot commented Feb 6, 2026

⚡️ This pull request contains optimizations for PR #10702

If you approve this dependent PR, these changes will be merged into the original PR branch pluggable-auth-service.

This PR will be automatically closed if the original PR is merged.


📄 2,105% (21.05x) speedup for get_optional_user_store_api_key in src/backend/base/langflow/api/v1/store.py

⏱️ Runtime : 44.9 milliseconds 2.04 milliseconds (best of 13 runs)

📝 Explanation and details

The optimized code achieves a 2104% speedup (from 44.9ms to 2.04ms) by implementing a thread-safe LRU cache for API key decryption results.

Key Optimization: Caching Expensive Decryption Operations

The original code calls _auth_service().decrypt_api_key() on every request, which is cryptographically expensive. The line profiler shows this operation taking 272,615 nanoseconds per hit and consuming 96% of the function's runtime.

The optimized version adds:

  • An OrderedDict to cache up to 1,024 encrypted→decrypted key mappings
  • A threading.Lock to ensure thread-safe cache access in concurrent environments
  • LRU eviction via move_to_end() and popitem(last=False) to maintain cache size

How It Works

On cache hit (subsequent requests with the same encrypted key):

  • The lookup takes only ~1,247 nanoseconds vs. the original 272,615 nanoseconds
  • The optimized decrypt_api_key now consumes just 0.00147 seconds total vs. 0.368 seconds in the original

The profiler confirms the dramatic improvement: in the optimized version, decrypt_api_key drops from 100% time spent in decryption to just cache lock acquisition and dictionary lookups.

Impact on Workloads

This optimization is particularly effective when:

  • Same users make repeated API calls (common in web applications where users authenticate once and make multiple requests)
  • Test case test_large_scale_many_users_decrypts_each_one_correctly with 500 users benefits significantly on subsequent runs with repeated keys
  • The 1,024-entry cache handles typical session-based workloads where a moderate number of active users make many requests

The optimization maintains correctness for exception handling (658 exceptions in profiler results) while still providing speedup on successful decryptions (869 successful calls benefited from caching).

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 513 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Click to see Generated Regression Tests
import types  # for SimpleNamespace to create lightweight user objects
from unittest.mock import Mock, patch  # to replace external dependencies

import pytest  # used for our unit tests
from langflow.api.v1.store import get_optional_user_store_api_key
from langflow.services.auth.utils import decrypt_api_key


def test_returns_none_when_store_api_key_is_none():
    # Create a user-like object with store_api_key set to None (falsy)
    user = types.SimpleNamespace(store_api_key=None)
    # Call the function and assert it returns None for None/empty keys
    codeflash_output = get_optional_user_store_api_key(user)


def test_returns_none_when_store_api_key_is_empty_string():
    # Empty string should be treated as falsy by the function and return None
    user = types.SimpleNamespace(store_api_key="")
    codeflash_output = get_optional_user_store_api_key(user)


@pytest.mark.parametrize(
    "falsy_value",
    [
        0,  # numeric zero is falsy and should yield None
        False,  # boolean False is falsy and should yield None
        [],  # empty list is falsy and should yield None
    ],
)
def test_returns_none_for_various_falsy_values(falsy_value):
    # Verify that any Python-falsy 'store_api_key' value causes the function to return None
    user = types.SimpleNamespace(store_api_key=falsy_value)
    codeflash_output = get_optional_user_store_api_key(user)


def test_successful_decryption_uses_auth_service_and_returns_decrypted_value():
    # Prepare a user with a non-empty (truthy) store_api_key
    encrypted = "encrypted_api_key_123"
    user = types.SimpleNamespace(store_api_key=encrypted)

    # Create a mock auth service with a decrypt_api_key method that returns a known value
    mock_service = Mock()
    mock_service.decrypt_api_key.return_value = "decrypted_value_abc"

    # Patch the get_auth_service used by the auth utils so decrypt_api_key is called on our mock
    with patch("langflow.services.deps.get_auth_service", return_value=mock_service) as patched_get:
        codeflash_output = get_optional_user_store_api_key(user); result = codeflash_output

        # Ensure the patched get_auth_service was actually used
        patched_get.assert_called_once()

        # Ensure decrypt_api_key was called with the original encrypted string
        mock_service.decrypt_api_key.assert_called_once_with(encrypted)


def test_decryption_exception_returns_original_store_api_key_and_logs_exception():
    # If decrypting raises an exception, the original store_api_key should be returned
    encrypted = "bad_encrypted_value"
    user = types.SimpleNamespace(store_api_key=encrypted)

    # Create a mock service whose decrypt_api_key raises an exception
    mock_service = Mock()
    mock_service.decrypt_api_key.side_effect = Exception("decryption failed")

    # Patch get_auth_service to return our mock that raises
    with patch("langflow.services.deps.get_auth_service", return_value=mock_service):
        codeflash_output = get_optional_user_store_api_key(user); result = codeflash_output




def test_large_scale_many_users_decrypts_each_one_correctly():
    # Large-scale test under the 1000-elements constraint (use 500 users)
    N = 500
    users = [types.SimpleNamespace(store_api_key=f"enc_{i}") for i in range(N)]

    # Prepare a mock service whose decrypt_api_key echoes with a prefix so we can verify mapping
    mock_service = Mock()
    mock_service.decrypt_api_key.side_effect = lambda enc: f"dec::{enc}"

    # Patch get_auth_service to return our mock service during the bulk calls
    with patch("langflow.services.deps.get_auth_service", return_value=mock_service):
        # For each user call the function and assert the decrypted mapping is correct
        for i, user in enumerate(users):
            expected = f"dec::enc_{i}"
            codeflash_output = get_optional_user_store_api_key(user); result = codeflash_output
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
from unittest.mock import Mock, patch

# imports
import pytest
from langflow.api.utils import CurrentActiveUser
from langflow.api.v1.store import get_optional_user_store_api_key


class TestGetOptionalUserStoreApiKeyBasic:
    """Basic test cases for get_optional_user_store_api_key function."""

To edit these changes git checkout codeflash/optimize-pr10702-2026-02-06T15.27.02 and push.

Codeflash

ogabrielluiz and others added 30 commits January 15, 2026 12:49
…ager for pluggable service discovery

- Added `register_service` decorator to allow services to self-register with the ServiceManager.
- Enhanced `ServiceManager` to support multiple service discovery mechanisms, including decorator-based registration, config files, and entry points.
- Implemented methods for direct service class registration and plugin discovery from various sources, improving flexibility and extensibility of service management.
- Introduced VariableService class to handle environment variables with in-memory caching.
- Added methods for getting, setting, deleting, and listing variables.
- Included logging for service initialization and variable operations.
- Created an __init__.py file to expose VariableService in the package namespace.
…teardown

- Updated LocalStorageService to inherit from both StorageService and Service for improved functionality.
- Added a name attribute for service identification.
- Implemented an async teardown method for future extensibility, even though no cleanup is currently needed.
- Refactored the constructor to ensure proper initialization of both parent classes.
…l logging functionality

- Added `BaseTelemetryService` as an abstract base class defining the interface for telemetry services.
- Introduced `TelemetryService`, a lightweight implementation that logs telemetry events without sending data.
- Created `__init__.py` to expose the telemetry service in the package namespace.
- Ensured robust async methods for logging various telemetry events and handling exceptions.
- Added `BaseTracingService` as an abstract base class defining the interface for tracing services.
- Implemented `TracingService`, a lightweight version that logs trace events without external integrations.
- Included async methods for starting and ending traces, tracing components, and managing logs and outputs.
- Enhanced documentation for clarity on method usage and parameters.
- Introduced a new test suite for validating the functionality of the @register_service decorator.
- Implemented tests for various service types including LocalStorageService, TelemetryService, and TracingService.
- Verified behavior for service registration with and without overrides, ensuring correct service management.
- Included tests for custom service implementations and preservation of class functionality.
- Enhanced overall test coverage for the service registration mechanism.
- Introduced a suite of unit tests covering edge cases for service registration, lifecycle management, and dependency resolution.
- Implemented integration tests to validate service loading from configuration files and environment variables.
- Enhanced test coverage for various service types including LocalStorageService, TelemetryService, and VariableService.
- Verified behavior for service registration with and without overrides, ensuring correct service management.
- Ensured robust handling of error conditions and edge cases in service creation and configuration parsing.
- Introduced comprehensive unit tests for LocalStorageService, TelemetryService, TracingService, and VariableService.
- Implemented integration tests to validate the interaction between minimal services.
- Ensured robust coverage for file operations, service readiness, and exception handling.
- Enhanced documentation within tests for clarity on functionality and expected behavior.
…ection

- Revised the documentation to highlight the advantages of the pluggable service system.
- Replaced the migration guide with a detailed overview of features such as automatic discovery, lazy instantiation, dependency injection, and lifecycle management.
- Clarified examples of service registration and improved overall documentation for better understanding.
During rebase, the teardown method was added in two locations (lines 57 and 220).
Removed the duplicate at line 57, keeping the one at the end of the class (line 220)
which is the more appropriate location for cleanup methods.
…changes

- Add MockSessionService fixtures to test files that use ServiceManager
- Update LocalStorageService test instantiation to use mock session and settings services
- Fix service count assertions to account for MockSessionService in fixtures
- Remove duplicate class-level clean_manager fixtures in test_edge_cases.py

These changes fix test failures caused by LocalStorageService requiring
session_service and settings_service parameters instead of just data_dir.
- Fixed Diamond Inheritance in LocalStorageService
- Added Circular Dependency Detection in _create_service_from_class
- Fixed StorageService.teardown to Have Default Implementation
- The aiofile library uses native async I/O (libaio) which fails with
  EAGAIN (SystemError: 11, 'Resource temporarily unavailable') in
  containerized environments like GitHub Actions runners.
- Switch to aiofiles which uses thread pool executors, providing reliable
  async file I/O across all environments including containers.
  The discover_plugins() method had a TOCTOU (time-of-check to time-of-use)
  race condition. Since get() uses a keyed lock (per service name), multiple
  threads requesting different services could concurrently see
  _plugins_discovered=False and trigger duplicate plugin discovery.

  Wrap discover_plugins() with self._lock to ensure thread-safe access to
  the _plugins_discovered flag and prevent concurrent discovery execution.
…ager for pluggable service discovery

- Added `register_service` decorator to allow services to self-register with the ServiceManager.
- Enhanced `ServiceManager` to support multiple service discovery mechanisms, including decorator-based registration, config files, and entry points.
- Implemented methods for direct service class registration and plugin discovery from various sources, improving flexibility and extensibility of service management.
…teardown

- Updated LocalStorageService to inherit from both StorageService and Service for improved functionality.
- Added a name attribute for service identification.
- Implemented an async teardown method for future extensibility, even though no cleanup is currently needed.
- Refactored the constructor to ensure proper initialization of both parent classes.
  Consolidate all authentication methods into the AuthService class to
  enable pluggable authentication implementations. The utils module now
  contains thin wrappers that delegate to the registered auth service.

  This allows alternative auth implementations (e.g., OIDC) to be
  registered via the pluggable services system while maintaining
  backward compatibility with existing code that imports from utils.

  Changes:
  - Move all auth logic (token creation, user validation, API key
    security, password hashing, encryption) to AuthService
  - Refactor utils.py to delegate to get_auth_service()
  - Update function signatures to remove settings_service parameter
    (now obtained from the service internally)
…vice parameter

  - Changed function to retrieve current user from access token instead of JWT.
  - Updated AuthServiceFactory to specify SettingsService type in create method.
  - Removed settings_service dependency from encryption and decryption functions, simplifying the code.

This refactor enhances the clarity and maintainability of the authentication logic.
- Introduced comprehensive unit tests for AuthService, covering token creation, user validation, and authentication methods.
- Added tests for pluggable authentication, ensuring correct delegation to registered services.
- Enhanced test coverage for user authentication scenarios, including active/inactive user checks and token validation.

These additions improve the reliability and maintainability of the authentication system.
autofix-ci Bot and others added 23 commits February 5, 2026 16:52
The optimized code achieves a **2104% speedup** (from 44.9ms to 2.04ms) by implementing a **thread-safe LRU cache** for API key decryption results.

## Key Optimization: Caching Expensive Decryption Operations

The original code calls `_auth_service().decrypt_api_key()` on every request, which is cryptographically expensive. The line profiler shows this operation taking **272,615 nanoseconds per hit** and consuming **96% of the function's runtime**.

The optimized version adds:
- An `OrderedDict` to cache up to 1,024 encrypted→decrypted key mappings
- A `threading.Lock` to ensure thread-safe cache access in concurrent environments
- LRU eviction via `move_to_end()` and `popitem(last=False)` to maintain cache size

## How It Works

On cache hit (subsequent requests with the same encrypted key):
- The lookup takes only **~1,247 nanoseconds** vs. the original **272,615 nanoseconds**
- The optimized `decrypt_api_key` now consumes just **0.00147 seconds** total vs. **0.368 seconds** in the original

The profiler confirms the dramatic improvement: in the optimized version, `decrypt_api_key` drops from **100% time** spent in decryption to just cache lock acquisition and dictionary lookups.

## Impact on Workloads

This optimization is particularly effective when:
- **Same users make repeated API calls** (common in web applications where users authenticate once and make multiple requests)
- **Test case `test_large_scale_many_users_decrypts_each_one_correctly`** with 500 users benefits significantly on subsequent runs with repeated keys
- The 1,024-entry cache handles typical session-based workloads where a moderate number of active users make many requests

The optimization maintains correctness for exception handling (658 exceptions in profiler results) while still providing speedup on successful decryptions (869 successful calls benefited from caching).
@codeflash-ai codeflash-ai Bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Feb 6, 2026
@github-actions github-actions Bot added the community Pull Request from an external contributor label Feb 6, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 66.14379% with 259 lines in your changes missing coverage. Please review.
✅ Project coverage is 32.96%. Comparing base (4063abd) to head (03e8340).
⚠️ Report is 83 commits behind head on main.

Files with missing lines Patch % Lines
src/backend/base/langflow/services/auth/service.py 56.54% 176 Missing ⚠️
src/lfx/src/lfx/services/auth/service.py 70.58% 15 Missing ⚠️
src/backend/base/langflow/services/auth/utils.py 85.13% 11 Missing ⚠️
src/lfx/src/lfx/services/auth/exceptions.py 62.50% 9 Missing ⚠️
src/backend/base/langflow/api/v1/login.py 30.00% 7 Missing ⚠️
src/backend/base/langflow/api/v1/users.py 30.00% 7 Missing ⚠️
...backend/base/langflow/services/variable/service.py 22.22% 7 Missing ⚠️
src/backend/base/langflow/api/v1/models.py 0.00% 4 Missing ⚠️
src/lfx/src/lfx/services/auth/base.py 93.22% 4 Missing ⚠️
src/backend/base/langflow/__main__.py 50.00% 3 Missing ⚠️
... and 10 more

❌ Your project status has failed because the head coverage (42.10%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main   #11624      +/-   ##
==========================================
- Coverage   34.94%   32.96%   -1.98%     
==========================================
  Files        1506     1512       +6     
  Lines       71834    72185     +351     
  Branches    10674    10672       -2     
==========================================
- Hits        25101    23795    -1306     
- Misses      45401    47058    +1657     
  Partials     1332     1332              
Flag Coverage Δ
lfx 42.10% <81.32%> (+0.26%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/backend/base/langflow/api/v1/api_key.py 73.33% <100.00%> (ø)
src/backend/base/langflow/api/v1/endpoints.py 53.11% <100.00%> (-19.79%) ⬇️
src/backend/base/langflow/api/v1/mcp.py 71.42% <100.00%> (-1.15%) ⬇️
src/backend/base/langflow/api/v2/schemas.py 100.00% <100.00%> (ø)
...c/backend/base/langflow/services/auth/constants.py 100.00% <100.00%> (ø)
...kend/base/langflow/services/auth/mcp_encryption.py 65.38% <100.00%> (-9.62%) ⬇️
src/backend/base/langflow/services/deps.py 84.93% <100.00%> (-2.03%) ⬇️
...rc/backend/base/langflow/services/event_manager.py 68.64% <100.00%> (-6.78%) ⬇️
src/backend/base/langflow/services/factory.py 83.60% <100.00%> (+0.55%) ⬆️
...kend/base/langflow/services/variable/kubernetes.py 0.00% <ø> (ø)
... and 25 more

... and 93 files with indirect coverage changes

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

Base automatically changed from pluggable-auth-service to main February 6, 2026 20:41
@ogabrielluiz
Copy link
Copy Markdown
Contributor

Closing automated codeflash PR.

@codeflash-ai codeflash-ai Bot deleted the codeflash/optimize-pr10702-2026-02-06T15.27.02 branch March 3, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI community Pull Request from an external contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants