Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Nov 7, 2025

📄 31% (0.31x) speedup for JiraDataSource.submit_bulk_transition in backend/python/app/sources/external/jira/jira.py

⏱️ Runtime : 2.00 millisecond 1.52 milliseconds (best of 250 runs)

📝 Explanation and details

The optimized code achieves a 31% runtime improvement by eliminating unnecessary dictionary operations and string conversions that were happening on every function call.

Key Optimizations Applied:

  1. Conditional URL Formatting: The original code always called _safe_format_url() even when _path was empty. The optimized version checks if _path has content first, avoiding the formatting overhead for the common case of no path parameters.

  2. Smart Dictionary Conversion:

    • For _query: Only calls _as_str_dict() when the dictionary is non-empty, otherwise uses an empty dict literal
    • For _headers: Checks if the dictionary already contains only string key-value pairs before converting, avoiding redundant work
    • For _path: When empty, uses a pre-allocated empty dict instead of calling _as_str_dict()
  3. Enhanced Helper Functions:

    • _safe_format_url() now has an early exit for empty parameters
    • _as_str_dict() includes fast-path logic for empty dictionaries and already-converted string dictionaries

Performance Impact Analysis:

The line profiler shows the optimization eliminated major bottlenecks:

  • _as_str_dict() calls dropped from consuming 100% of helper function time to being conditionally avoided
  • URL formatting overhead was reduced by skipping unnecessary template processing
  • Dictionary allocations were minimized through better conditional logic

Test Case Performance:
The optimizations are particularly effective for:

  • Throughput tests: All concurrent execution scenarios benefit from reduced per-call overhead
  • Large-scale tests: The 31% improvement compounds when processing many requests
  • Basic operations: Even simple calls see immediate benefits from avoiding unnecessary conversions

The optimization maintains identical functionality and error handling while significantly reducing computational overhead, making it especially valuable for high-volume Jira API operations where this function would be called repeatedly.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 487 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 93.3%
🌀 Generated Regression Tests and Runtime
import asyncio  # Used to run async functions

import pytest  # Used for our unit tests
from app.sources.external.jira.jira import JiraDataSource


# ---- Minimal stubs for HTTPRequest and HTTPResponse ----
class HTTPRequest:
    def __init__(self, method, url, headers, path_params, query_params, body):
        self.method = method
        self.url = url
        self.headers = headers
        self.path_params = path_params
        self.query_params = query_params
        self.body = body

class HTTPResponse:
    def __init__(self, response_data):
        self.data = response_data

# ---- Minimal stub for JiraClient and HTTPClient ----
class DummyAsyncClient:
    async def request(self, method, url, **kwargs):
        # Simulate a response object
        return {'method': method, 'url': url, 'kwargs': kwargs}

class DummyHTTPClient:
    def __init__(self, base_url="https://example.atlassian.net", token="dummy", token_type="Bearer"):
        self.base_url = base_url
        self.headers = {"Authorization": f"{token_type} {token}"}
        self.client = DummyAsyncClient()

    def get_base_url(self):
        return self.base_url

    async def execute(self, request, **kwargs):
        # Simulate async HTTP execution, return HTTPResponse with request info for validation
        response_data = {
            'method': request.method,
            'url': request.url,
            'headers': request.headers,
            'path_params': request.path_params,
            'query_params': request.query_params,
            'body': request.body,
        }
        return HTTPResponse(response_data)

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        pass

class JiraClient:
    def __init__(self, client):
        self.client = client

    def get_client(self):
        return self.client
from app.sources.external.jira.jira import JiraDataSource

# ---- UNIT TESTS ----

@pytest.mark.asyncio
async def test_submit_bulk_transition_basic_success():
    """Basic: Should succeed with typical input and return correct HTTPResponse data."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': '10001', 'transition': {'id': '21'}}]
    resp = await ds.submit_bulk_transition(bulk_inputs)

@pytest.mark.asyncio
async def test_submit_bulk_transition_with_sendBulkNotification_true():
    """Basic: Should include sendBulkNotification in body if set to True."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': '10002', 'transition': {'id': '22'}}]
    resp = await ds.submit_bulk_transition(bulk_inputs, sendBulkNotification=True)

@pytest.mark.asyncio
async def test_submit_bulk_transition_with_custom_headers():
    """Basic: Should merge custom headers and set Content-Type."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    custom_headers = {'X-Test-Header': 'value'}
    resp = await ds.submit_bulk_transition([{'issueId': '10003', 'transition': {'id': '23'}}], headers=custom_headers)

@pytest.mark.asyncio
async def test_submit_bulk_transition_empty_bulk_inputs():
    """Edge: Should handle empty bulkTransitionInputs gracefully."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    resp = await ds.submit_bulk_transition([])

@pytest.mark.asyncio
async def test_submit_bulk_transition_invalid_client_raises():
    """Edge: Should raise ValueError if client is None."""
    class BadClient:
        def get_client(self):
            return None
    with pytest.raises(ValueError) as excinfo:
        JiraDataSource(BadClient())

@pytest.mark.asyncio
async def test_submit_bulk_transition_missing_get_base_url_raises():
    """Edge: Should raise ValueError if client lacks get_base_url method."""
    class BadHTTPClient:
        pass
    with pytest.raises(ValueError) as excinfo:
        JiraDataSource(JiraClient(BadHTTPClient()))

@pytest.mark.asyncio
async def test_submit_bulk_transition_concurrent_execution():
    """Edge: Should handle multiple concurrent requests correctly."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [
        [{'issueId': '10004', 'transition': {'id': '24'}}],
        [{'issueId': '10005', 'transition': {'id': '25'}}],
        [{'issueId': '10006', 'transition': {'id': '26'}}],
    ]
    tasks = [ds.submit_bulk_transition(inputs) for inputs in bulk_inputs]
    results = await asyncio.gather(*tasks)
    for i, resp in enumerate(results):
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_concurrent_with_different_headers():
    """Edge: Concurrent requests with different headers should preserve each header."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    tasks = [
        ds.submit_bulk_transition([{'issueId': '10007', 'transition': {'id': '27'}}], headers={'X-Req': 'A'}),
        ds.submit_bulk_transition([{'issueId': '10008', 'transition': {'id': '28'}}], headers={'X-Req': 'B'}),
    ]
    results = await asyncio.gather(*tasks)

@pytest.mark.asyncio
async def test_submit_bulk_transition_large_bulk_inputs():
    """Large Scale: Should handle a large number of bulkTransitionInputs."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': str(i), 'transition': {'id': str(20+i)}} for i in range(100)]
    resp = await ds.submit_bulk_transition(bulk_inputs)

@pytest.mark.asyncio
async def test_submit_bulk_transition_concurrent_large_scale():
    """Large Scale: Multiple concurrent requests with large bulkTransitionInputs."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs_list = [
        [{'issueId': str(i), 'transition': {'id': str(30+i)}} for i in range(50)],
        [{'issueId': str(i), 'transition': {'id': str(80+i)}} for i in range(80)],
    ]
    tasks = [ds.submit_bulk_transition(inputs) for inputs in bulk_inputs_list]
    results = await asyncio.gather(*tasks)
    for i, resp in enumerate(results):
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_throughput_small_load():
    """Throughput: Measure response for small load (10 requests)."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': str(i), 'transition': {'id': str(40+i)}} for i in range(2)]
    tasks = [ds.submit_bulk_transition(bulk_inputs) for _ in range(10)]
    results = await asyncio.gather(*tasks)
    for resp in results:
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_throughput_medium_load():
    """Throughput: Measure response for medium load (50 requests)."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': str(i), 'transition': {'id': str(50+i)}} for i in range(5)]
    tasks = [ds.submit_bulk_transition(bulk_inputs) for _ in range(50)]
    results = await asyncio.gather(*tasks)
    for resp in results:
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_throughput_high_volume():
    """Throughput: Measure response for high volume (100 requests, each with 10 bulk inputs)."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': str(i), 'transition': {'id': str(60+i)}} for i in range(10)]
    tasks = [ds.submit_bulk_transition(bulk_inputs) for _ in range(100)]
    results = await asyncio.gather(*tasks)
    for resp in results:
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_body_includes_sendBulkNotification_false():
    """Edge: Should include sendBulkNotification in body if set to False."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    bulk_inputs = [{'issueId': '10009', 'transition': {'id': '29'}}]
    resp = await ds.submit_bulk_transition(bulk_inputs, sendBulkNotification=False)

@pytest.mark.asyncio
async def test_submit_bulk_transition_headers_content_type_override():
    """Edge: Should override Content-Type if provided in headers."""
    client = JiraClient(DummyHTTPClient())
    ds = JiraDataSource(client)
    resp = await ds.submit_bulk_transition(
        [{'issueId': '10010', 'transition': {'id': '30'}}],
        headers={'Content-Type': 'application/xml'}
    )
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import asyncio  # used to run async functions

import pytest  # used for our unit tests
from app.sources.external.jira.jira import JiraDataSource


# --- Minimal stubs for HTTPRequest and HTTPResponse ---
class HTTPRequest:
    def __init__(self, method, url, headers, path_params, query_params, body):
        self.method = method
        self.url = url
        self.headers = headers
        self.path_params = path_params
        self.query_params = query_params
        self.body = body

class HTTPResponse:
    def __init__(self, response):
        self.response = response

    def json(self):
        return self.response  # For testing, just return the input dict

# --- Minimal stub for JiraClient and its underlying REST client ---
class DummyJiraRESTClient:
    def __init__(self, base_url):
        self.base_url = base_url
        self.executed_requests = []

    def get_base_url(self):
        return self.base_url

    async def execute(self, request, **kwargs):
        # Simulate a response containing the request data for verification
        self.executed_requests.append(request)
        # Simulate a realistic response structure
        return HTTPResponse({
            "method": request.method,
            "url": request.url,
            "headers": request.headers,
            "body": request.body,
        })

class JiraClient:
    def __init__(self, client):
        self.client = client

    def get_client(self):
        return self.client
from app.sources.external.jira.jira import JiraDataSource

# --- TESTS ---

# ---- Basic Test Cases ----

@pytest.mark.asyncio
async def test_submit_bulk_transition_basic_success():
    """Test basic successful call with minimal required arguments."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    # Minimal valid input
    bulk_inputs = [{"issueId": "123", "transition": "done"}]
    resp = await ds.submit_bulk_transition(bulk_inputs)

@pytest.mark.asyncio
async def test_submit_bulk_transition_with_optional_args():
    """Test with all optional arguments provided."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com/"))
    ds = JiraDataSource(client)
    bulk_inputs = [{"issueId": "456", "transition": "in_progress"}]
    headers = {"Custom-Header": "value"}
    resp = await ds.submit_bulk_transition(bulk_inputs, sendBulkNotification=True, headers=headers)

@pytest.mark.asyncio
async def test_submit_bulk_transition_empty_bulk_inputs():
    """Test with empty bulkTransitionInputs list."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs = []
    resp = await ds.submit_bulk_transition(bulk_inputs)

# ---- Edge Test Cases ----

@pytest.mark.asyncio
async def test_submit_bulk_transition_none_client_raises():
    """Test that ValueError is raised if client is None."""
    class DummyClientNone:
        def get_client(self):
            return None
    with pytest.raises(ValueError, match="HTTP client is not initialized"):
        JiraDataSource(DummyClientNone())

@pytest.mark.asyncio
async def test_submit_bulk_transition_missing_get_base_url_raises():
    """Test that ValueError is raised if client does not have get_base_url."""
    class DummyClientNoBaseUrl:
        def get_client(self):
            return object()
    with pytest.raises(ValueError, match="HTTP client does not have get_base_url method"):
        JiraDataSource(DummyClientNoBaseUrl())

@pytest.mark.asyncio
async def test_submit_bulk_transition_concurrent_execution():
    """Test concurrent execution of multiple submit_bulk_transition calls."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs1 = [{"issueId": "1", "transition": "done"}]
    bulk_inputs2 = [{"issueId": "2", "transition": "in_progress"}]
    # Run two calls concurrently
    results = await asyncio.gather(
        ds.submit_bulk_transition(bulk_inputs1),
        ds.submit_bulk_transition(bulk_inputs2, sendBulkNotification=False)
    )

@pytest.mark.asyncio
async def test_submit_bulk_transition_headers_override_content_type():
    """Test that headers override Content-Type correctly."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs = [{"issueId": "3", "transition": "review"}]
    # Override Content-Type
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    resp = await ds.submit_bulk_transition(bulk_inputs, headers=headers)

@pytest.mark.asyncio
async def test_submit_bulk_transition_invalid_bulk_inputs_type():
    """Test that function accepts non-list bulkTransitionInputs (should send as is)."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    # Pass a tuple instead of list
    bulk_inputs = ({"issueId": "4", "transition": "closed"},)
    resp = await ds.submit_bulk_transition(bulk_inputs)

# ---- Large Scale Test Cases ----

@pytest.mark.asyncio
async def test_submit_bulk_transition_large_bulk_inputs():
    """Test with a large list of bulkTransitionInputs."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs = [{"issueId": str(i), "transition": "done"} for i in range(100)]  # 100 is reasonable
    resp = await ds.submit_bulk_transition(bulk_inputs)

@pytest.mark.asyncio
async def test_submit_bulk_transition_many_concurrent_calls():
    """Test many concurrent submit_bulk_transition calls."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    # 20 concurrent calls
    bulk_inputs_list = [[{"issueId": str(i), "transition": "done"}] for i in range(20)]
    tasks = [ds.submit_bulk_transition(inputs) for inputs in bulk_inputs_list]
    results = await asyncio.gather(*tasks)
    for i, resp in enumerate(results):
        pass

# ---- Throughput Test Cases ----

@pytest.mark.asyncio
async def test_submit_bulk_transition_throughput_small_load():
    """Throughput test: small load (5 requests)."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs_list = [[{"issueId": str(i), "transition": "done"}] for i in range(5)]
    tasks = [ds.submit_bulk_transition(inputs) for inputs in bulk_inputs_list]
    results = await asyncio.gather(*tasks)
    for i, resp in enumerate(results):
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_throughput_medium_load():
    """Throughput test: medium load (50 requests)."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs_list = [[{"issueId": str(i), "transition": "done"}] for i in range(50)]
    tasks = [ds.submit_bulk_transition(inputs) for inputs in bulk_inputs_list]
    results = await asyncio.gather(*tasks)
    for i, resp in enumerate(results):
        pass

@pytest.mark.asyncio
async def test_submit_bulk_transition_throughput_large_load():
    """Throughput test: large load (200 requests)."""
    client = JiraClient(DummyJiraRESTClient("https://jira.example.com"))
    ds = JiraDataSource(client)
    bulk_inputs_list = [[{"issueId": str(i), "transition": "done"}] for i in range(200)]
    tasks = [ds.submit_bulk_transition(inputs) for inputs in bulk_inputs_list]
    results = await asyncio.gather(*tasks)
    for i, resp in enumerate(results):
        pass

# ---- Additional async-specific edge case ----

@pytest.mark.asyncio
async def test_submit_bulk_transition_async_context_manager_usage():
    """Test that JiraDataSource works inside an async context manager pattern."""
    # Simulate async context manager for client (not strictly required by function, but realistic usage)
    class DummyAsyncRESTClient(DummyJiraRESTClient):
        async def __aenter__(self):
            return self
        async def __aexit__(self, exc_type, exc_val, exc_tb):
            pass

    async with DummyAsyncRESTClient("https://jira.example.com") as rest_client:
        client = JiraClient(rest_client)
        ds = JiraDataSource(client)
        bulk_inputs = [{"issueId": "xyz", "transition": "done"}]
        resp = await ds.submit_bulk_transition(bulk_inputs)
# 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-JiraDataSource.submit_bulk_transition-mhorfcrs and push.

Codeflash Static Badge

The optimized code achieves a **31% runtime improvement** by eliminating unnecessary dictionary operations and string conversions that were happening on every function call.

**Key Optimizations Applied:**

1. **Conditional URL Formatting**: The original code always called `_safe_format_url()` even when `_path` was empty. The optimized version checks if `_path` has content first, avoiding the formatting overhead for the common case of no path parameters.

2. **Smart Dictionary Conversion**: 
   - For `_query`: Only calls `_as_str_dict()` when the dictionary is non-empty, otherwise uses an empty dict literal
   - For `_headers`: Checks if the dictionary already contains only string key-value pairs before converting, avoiding redundant work
   - For `_path`: When empty, uses a pre-allocated empty dict instead of calling `_as_str_dict()`

3. **Enhanced Helper Functions**:
   - `_safe_format_url()` now has an early exit for empty parameters
   - `_as_str_dict()` includes fast-path logic for empty dictionaries and already-converted string dictionaries

**Performance Impact Analysis:**

The line profiler shows the optimization eliminated major bottlenecks:
- `_as_str_dict()` calls dropped from consuming 100% of helper function time to being conditionally avoided
- URL formatting overhead was reduced by skipping unnecessary template processing
- Dictionary allocations were minimized through better conditional logic

**Test Case Performance:**
The optimizations are particularly effective for:
- **Throughput tests**: All concurrent execution scenarios benefit from reduced per-call overhead
- **Large-scale tests**: The 31% improvement compounds when processing many requests
- **Basic operations**: Even simple calls see immediate benefits from avoiding unnecessary conversions

The optimization maintains identical functionality and error handling while significantly reducing computational overhead, making it especially valuable for high-volume Jira API operations where this function would be called repeatedly.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 November 7, 2025 11:16
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: Medium Optimization Quality according to Codeflash labels Nov 7, 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: Medium Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant