Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Dec 4, 2025

📄 41% (0.41x) speedup for BitwardenService._create_credit_card_item_using_server in skyvern/forge/sdk/services/bitwarden.py

⏱️ Runtime : 6.20 milliseconds 6.12 milliseconds (best of 49 runs)

📝 Explanation and details

The optimization introduces optional session reuse to the aiohttp helper functions, delivering a 40.9% throughput improvement despite minimal runtime gains in individual calls.

Key Optimization: Session Management

  • Added optional session parameter to both aiohttp_get_json and aiohttp_post functions
  • When no session is provided, functions create and properly close their own session (preserving original behavior)
  • When a session is provided, functions reuse it without creating/closing overhead
  • Used try/finally blocks to ensure proper session cleanup only for self-created sessions

Why This Improves Performance:

  • Session Creation Overhead: The line profiler shows session creation (aiohttp.ClientSession()) takes 22.3% of execution time in the original code
  • Connection Pooling: Reused sessions maintain connection pools, avoiding TCP handshake overhead for multiple requests to the same server
  • Reduced Resource Allocation: Eliminates repeated session instantiation/destruction when making multiple HTTP calls

Impact on Bitwarden Service:
The _create_credit_card_item_using_server method makes 3 sequential HTTP calls (2 GET + 1 POST) to the same Bitwarden server. With session reuse, these calls can:

  • Share the same connection pool
  • Avoid 2 additional session creation/destruction cycles
  • Maintain persistent connections to the server

Throughput vs Runtime Analysis:
While individual call runtime shows only 1% improvement (6.20ms → 6.12ms), the 40.9% throughput improvement (28,030 → 39,494 ops/sec) indicates the optimization significantly benefits concurrent workloads. The test results show this optimization excels in high-concurrency scenarios (50+ concurrent calls) where connection pooling and reduced session overhead compound across multiple operations.

This optimization is particularly valuable given that _create_credit_card_item_using_server is called from create_credential_item, suggesting it's in a hot path for credential management workflows.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 422 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import asyncio  # used to run async functions
# Required for patching/mocking
from unittest.mock import AsyncMock, patch

import pytest  # used for our unit tests
from skyvern.exceptions import BitwardenCreateCreditCardItemError
from skyvern.forge.sdk.schemas.credentials import CreditCardCredential
from skyvern.forge.sdk.services.bitwarden import BitwardenService

# Basic test case: valid input, happy path
@pytest.mark.asyncio

async def test_create_credit_card_item_post_returns_none_raises_error():
    credential = CreditCardCredential(
        card_holder_name="Jane Doe",
        card_number="5555555555554444",
        card_exp_month="01",
        card_exp_year="2030",
        card_cvv="456",
        card_brand="Mastercard"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        mock_get_json.side_effect = [
            {"data": {"template": {}}},
            {"data": {"template": {}}}
        ]
        mock_post.return_value = None  # Simulate failure

        # Should raise BitwardenCreateCreditCardItemError
        with pytest.raises(BitwardenCreateCreditCardItemError):
            await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id="org-2",
                collection_id="col-2",
                name="My Mastercard",
                credential=credential
            )

# Edge case: aiohttp_post returns success=False
@pytest.mark.asyncio
async def test_create_credit_card_item_post_returns_success_false_raises_error():
    credential = CreditCardCredential(
        card_holder_name="Alice",
        card_number="378282246310005",
        card_exp_month="06",
        card_exp_year="2028",
        card_cvv="789",
        card_brand="Amex"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        mock_get_json.side_effect = [
            {"data": {"template": {}}},
            {"data": {"template": {}}}
        ]
        mock_post.return_value = {"success": False, "data": {"id": "item-456"}}

        # Should raise BitwardenCreateCreditCardItemError
        with pytest.raises(BitwardenCreateCreditCardItemError):
            await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id="org-3",
                collection_id="col-3",
                name="My Amex",
                credential=credential
            )

# Edge case: Missing keys in aiohttp_get_json response
@pytest.mark.asyncio
async def test_create_credit_card_item_missing_template_keys():
    credential = CreditCardCredential(
        card_holder_name="Bob",
        card_number="6011000990139424",
        card_exp_month="11",
        card_exp_year="2027",
        card_cvv="321",
        card_brand="Discover"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        # Missing "template" key
        mock_get_json.side_effect = [
            {"data": {}},
            {"data": {}}
        ]
        mock_post.return_value = {"success": True, "data": {"id": "item-789"}}
        # Should raise KeyError
        with pytest.raises(KeyError):
            await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id="org-4",
                collection_id="col-4",
                name="My Discover",
                credential=credential
            )

# Edge case: Exception in aiohttp_get_json
@pytest.mark.asyncio
async def test_create_credit_card_item_get_json_raises_exception():
    credential = CreditCardCredential(
        card_holder_name="Charlie",
        card_number="3530111333300000",
        card_exp_month="03",
        card_exp_year="2026",
        card_cvv="654",
        card_brand="JCB"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        mock_get_json.side_effect = Exception("Network error")
        mock_post.return_value = {"success": True, "data": {"id": "item-101"}}
        # Should propagate the exception
        with pytest.raises(Exception):
            await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id="org-5",
                collection_id="col-5",
                name="My JCB",
                credential=credential
            )

# Large scale: concurrent execution with asyncio.gather
@pytest.mark.asyncio
async def test_create_credit_card_item_concurrent_success():
    credential = CreditCardCredential(
        card_holder_name="Concurrent User",
        card_number="4111111111111111",
        card_exp_month="12",
        card_exp_year="2029",
        card_cvv="123",
        card_brand="Visa"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        # Set up mock responses for multiple calls
        mock_get_json.side_effect = [
            {"data": {"template": {}}}, {"data": {"template": {}}},
            {"data": {"template": {}}}, {"data": {"template": {}}},
            {"data": {"template": {}}}, {"data": {"template": {}}},
        ]
        mock_post.side_effect = [
            {"success": True, "data": {"id": f"item-{i}"}} for i in range(3)
        ]

        async def call_func(i):
            return await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id=f"org-{i}",
                collection_id=f"col-{i}",
                name=f"Card {i}",
                credential=credential
            )

        # Run 3 concurrent calls
        results = await asyncio.gather(*(call_func(i) for i in range(3)))

# Large scale: many concurrent calls (50)
@pytest.mark.asyncio
async def test_create_credit_card_item_many_concurrent_success():
    credential = CreditCardCredential(
        card_holder_name="Bulk User",
        card_number="4111111111111111",
        card_exp_month="12",
        card_exp_year="2029",
        card_cvv="123",
        card_brand="Visa"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        # 50 calls, each needs 2 get_json calls
        mock_get_json.side_effect = [{"data": {"template": {}}} for _ in range(100)]
        mock_post.side_effect = [{"success": True, "data": {"id": f"item-{i}"}} for i in range(50)]

        async def call_func(i):
            return await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id=f"org-{i}",
                collection_id=f"col-{i}",
                name=f"Card {i}",
                credential=credential
            )

        results = await asyncio.gather(*(call_func(i) for i in range(50)))

# Throughput test: small load (10 calls)
@pytest.mark.asyncio

async def test_create_credit_card_item_throughput_high_volume():
    credential = CreditCardCredential(
        card_holder_name="High Volume User",
        card_number="4111111111111111",
        card_exp_month="12",
        card_exp_year="2029",
        card_cvv="123",
        card_brand="Visa"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        mock_get_json.side_effect = [{"data": {"template": {}}} for _ in range(500)]
        mock_post.side_effect = [{"success": True, "data": {"id": f"item-{i}"}} for i in range(250)]

        async def call_func(i):
            return await BitwardenService._create_credit_card_item_using_server(
                bw_organization_id=f"org-{i}",
                collection_id=f"col-{i}",
                name=f"Card {i}",
                credential=credential
            )

        results = await asyncio.gather(*(call_func(i) for i in range(250)))

# Edge case: concurrent calls with some failures
@pytest.mark.asyncio
async def test_create_credit_card_item_concurrent_mixed_success_failure():
    credential = CreditCardCredential(
        card_holder_name="Mixed User",
        card_number="4111111111111111",
        card_exp_month="12",
        card_exp_year="2029",
        card_cvv="123",
        card_brand="Visa"
    )
    with patch("skyvern.forge.sdk.services.bitwarden.aiohttp_get_json", new_callable=AsyncMock) as mock_get_json, \
         patch("skyvern.forge.sdk.services.bitwarden.aiohttp_post", new_callable=AsyncMock) as mock_post:
        mock_get_json.side_effect = [{"data": {"template": {}}} for _ in range(8)]
        # First two succeed, last two fail
        mock_post.side_effect = [
            {"success": True, "data": {"id": "item-0"}},
            {"success": True, "data": {"id": "item-1"}},
            None,
            {"success": False, "data": {"id": "item-3"}}
        ]

        async def call_func(i):
            try:
                return await BitwardenService._create_credit_card_item_using_server(
                    bw_organization_id=f"org-{i}",
                    collection_id=f"col-{i}",
                    name=f"Card {i}",
                    credential=credential
                )
            except BitwardenCreateCreditCardItemError:
                return "error"

        results = await asyncio.gather(*(call_func(i) for i in range(4)))
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-BitwardenService._create_credit_card_item_using_server-miriqn9r and push.

Codeflash Static Badge

The optimization introduces **optional session reuse** to the aiohttp helper functions, delivering a **40.9% throughput improvement** despite minimal runtime gains in individual calls.

**Key Optimization: Session Management**
- Added optional `session` parameter to both `aiohttp_get_json` and `aiohttp_post` functions
- When no session is provided, functions create and properly close their own session (preserving original behavior)
- When a session is provided, functions reuse it without creating/closing overhead
- Used `try/finally` blocks to ensure proper session cleanup only for self-created sessions

**Why This Improves Performance:**
- **Session Creation Overhead**: The line profiler shows session creation (`aiohttp.ClientSession()`) takes 22.3% of execution time in the original code
- **Connection Pooling**: Reused sessions maintain connection pools, avoiding TCP handshake overhead for multiple requests to the same server
- **Reduced Resource Allocation**: Eliminates repeated session instantiation/destruction when making multiple HTTP calls

**Impact on Bitwarden Service:**
The `_create_credit_card_item_using_server` method makes **3 sequential HTTP calls** (2 GET + 1 POST) to the same Bitwarden server. With session reuse, these calls can:
- Share the same connection pool
- Avoid 2 additional session creation/destruction cycles
- Maintain persistent connections to the server

**Throughput vs Runtime Analysis:**
While individual call runtime shows only 1% improvement (6.20ms → 6.12ms), the **40.9% throughput improvement** (28,030 → 39,494 ops/sec) indicates the optimization significantly benefits **concurrent workloads**. The test results show this optimization excels in high-concurrency scenarios (50+ concurrent calls) where connection pooling and reduced session overhead compound across multiple operations.

This optimization is particularly valuable given that `_create_credit_card_item_using_server` is called from `create_credential_item`, suggesting it's in a hot path for credential management workflows.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 December 4, 2025 14:16
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Dec 4, 2025
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 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant