Skip to content

Conversation

@ketkarameya
Copy link

@ketkarameya ketkarameya commented Feb 10, 2026

Code Review Bench PR langgenius#30339 - feat: get plan bulk with cache

Benchmark PR for Gitar evaluation

Original PR: agentic-review-benchmarks#10

feat: get plan bulk with cache


Summary by Gitar

  • New caching layer:
    • get_plan_bulk_with_cache() in api/services/billing_service.py with Redis backend and 10-minute TTL
    • Uses batch operations (mget/pipeline) to minimize Redis round-trips
  • Enhanced error handling:
    • Graceful degradation for Redis failures, invalid JSON, and schema validation errors
    • Invalid tenants skipped in get_plan_bulk() instead of returning None
  • Comprehensive testing:
    • 9 integration tests with testcontainers (real Redis) covering cache hits/misses/failures
    • 1 unit test for validation error handling (402 total test lines)

This will update automatically on new commits.


logger.exception(
"get_plan_bulk: failed to validate subscription plan for tenant(%s)", tenant_id
)
results[tenant_id] = None

Choose a reason for hiding this comment

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

⚠️ Bug: get_plan_bulk sets None instead of skipping invalid tenants

On line 292, when validation fails for a tenant's plan data, the code sets results[tenant_id] = None. This contradicts the intended behavior (skip invalid tenants) and has two downstream consequences:

  1. Test failure: The unit test test_get_plan_bulk_with_invalid_tenant_plan_skipped asserts "tenant-invalid" not in result and len(result) == 2, but the code adds the invalid tenant with value None, so len(result) will be 3 and the assertion will fail.

  2. Type violation: get_plan_bulk is typed as returning dict[str, SubscriptionPlan], but it returns None values. This propagates into get_plan_bulk_with_cache which will add None to tenant_plans, cache "null" in Redis (via json.dumps(None)), and on the next cache read, validate_python(None) will fail, causing a perpetual miss-refetch-cache cycle for that tenant.

The fix is to use continue instead of assigning None.

Was this helpful? React with 👍 / 👎

Suggested change
results[tenant_id] = None
except Exception:
logger.exception(
"get_plan_bulk: failed to validate subscription plan for tenant(%s)", tenant_id
)
continue
  • Apply suggested fix

@gitar-bot-staging
Copy link

gitar-bot-staging bot commented Feb 10, 2026

Code Review ⚠️ Changes requested 0 resolved / 2 findings

The get_plan_bulk error handler at line 292 assigns None instead of skipping invalid tenants, violating the return type, failing the corresponding unit test, and causing a cache pollution cycle when used by get_plan_bulk_with_cache. Replace results[tenant_id] = None with continue.

⚠️ Bug: get_plan_bulk sets None instead of skipping invalid tenants

📄 api/services/billing_service.py:292

On line 292, when validation fails for a tenant's plan data, the code sets results[tenant_id] = None. This contradicts the intended behavior (skip invalid tenants) and has two downstream consequences:

  1. Test failure: The unit test test_get_plan_bulk_with_invalid_tenant_plan_skipped asserts "tenant-invalid" not in result and len(result) == 2, but the code adds the invalid tenant with value None, so len(result) will be 3 and the assertion will fail.

  2. Type violation: get_plan_bulk is typed as returning dict[str, SubscriptionPlan], but it returns None values. This propagates into get_plan_bulk_with_cache which will add None to tenant_plans, cache "null" in Redis (via json.dumps(None)), and on the next cache read, validate_python(None) will fail, causing a perpetual miss-refetch-cache cycle for that tenant.

The fix is to use continue instead of assigning None.

Suggested fix
                    except Exception:
                        logger.exception(
                            "get_plan_bulk: failed to validate subscription plan for tenant(%s)", tenant_id
                        )
                        continue
💡 Edge Case: None values from get_plan_bulk cached and returned as plans

📄 api/services/billing_service.py:365

Since get_plan_bulk currently sets results[tenant_id] = None for validation failures (line 292), get_plan_bulk_with_cache will blindly iterate over these None entries at line 365-367, adding them to both tenant_plans (returned to the caller) and plans_to_cache. Then json.dumps(None) produces "null", which gets cached in Redis. On the next cache read, json.loads("null") returns None, and validate_python(None) will fail, treating it as a cache miss — causing a repeated cache-miss → fetch → cache-"null" cycle.

This is a direct consequence of the line 292 bug. Once line 292 is fixed to continue instead of assigning None, this issue is also resolved. However, as a defensive measure, you could also add a None check in the caching loop:

for tenant_id, subscription_plan in bulk_plans.items():
    if subscription_plan is not None:
        tenant_plans[tenant_id] = subscription_plan
        plans_to_cache[tenant_id] = subscription_plan
Rules 🎸 1 action taken

Gitar Rules

🎸 Summary Enhancement: Updated PR description with technical summary (Redis caching layer, error handling, comprehensive testing)

5 rules not applicable. Show all rules by commenting gitar display:verbose.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copy link

@gitar-bot-staging gitar-bot-staging bot left a comment

Choose a reason for hiding this comment

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

⚠️ This PR is blocked due to unresolved code review findings.

Configure merge blocking · Maintainers can dismiss this review. Gitar never approves changes.

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.

4 participants