Skip to content

Commit 71d861a

Browse files
committed
Delay initial processing and retry via Cloud Tasks when the revision is not yet public
1 parent ed0804f commit 71d861a

4 files changed

Lines changed: 42 additions & 2 deletions

File tree

services/reviewhelper-api/app/review_processor.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from collections.abc import AsyncIterator
3+
from datetime import UTC, datetime, timedelta
34
from functools import cache
45
from typing import Collection, Iterable
56

@@ -13,11 +14,20 @@
1314

1415
logger = logging.getLogger(__name__)
1516

17+
VISIBILITY_TIMEOUT = timedelta(minutes=5)
18+
1619

1720
class ReviewProcessingError(Exception):
1821
"""Custom exception for permanent errors during review processing that should not trigger retries."""
1922

2023

24+
class RevisionNotYetPublicError(Exception):
25+
"""The revision is not yet public but was recently created.
26+
27+
This is a transient error — Cloud Tasks should retry after backoff.
28+
"""
29+
30+
2131
@cache
2232
def get_code_review_tool():
2333
from bugbug.tools.code_review import CodeReviewTool
@@ -50,6 +60,15 @@ async def process_review(
5060
raise ValueError(f"Unsupported platform: {review_request.platform}")
5161

5262
if not patch.is_accessible() or not patch.is_public():
63+
if (
64+
review_request.created_at
65+
and datetime.now(UTC) - review_request.created_at < VISIBILITY_TIMEOUT
66+
):
67+
raise RevisionNotYetPublicError(
68+
f"Revision D{review_request.revision_id} is not yet public. "
69+
f"Waiting for phab-bot to update visibility."
70+
)
71+
5372
raise ReviewProcessingError(
5473
"Unable to access the revision. This may be because "
5574
"the revision is private or has restricted visibility."

services/reviewhelper-api/app/routers/request.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from datetime import timedelta
23
from typing import Annotated
34

45
from fastapi import APIRouter, Depends, status
@@ -15,6 +16,12 @@
1516

1617
logger = logging.getLogger(__name__)
1718

19+
# For newly-created revisions, delay the task to give phab-bot time to
20+
# update visibility before we attempt processing. If the delay has
21+
# already elapsed, Cloud Tasks dispatches immediately.
22+
INITIAL_TASK_DELAY = timedelta(seconds=30)
23+
24+
1825
router = APIRouter(
1926
tags=["request"],
2027
dependencies=[Depends(verify_external_api_key)],
@@ -75,8 +82,14 @@ async def create_or_get_review_request(
7582
db.add(review_request)
7683
await db.commit()
7784

85+
schedule_time = (
86+
request.revision_created_at + INITIAL_TASK_DELAY
87+
if request.revision_created_at
88+
else None
89+
)
90+
7891
# Queue task for processing
79-
await create_review_task(review_request.id)
92+
await create_review_task(review_request.id, schedule_time=schedule_time)
8093

8194
return JSONResponse(
8295
{

services/reviewhelper-api/app/schemas/review_request.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from datetime import datetime
2+
13
from pydantic import BaseModel
24

35
from app.enums import ReviewStatus
@@ -9,6 +11,7 @@ class ReviewRequestCreate(UserActionBase):
911

1012
revision_id: int
1113
diff_id: int
14+
revision_created_at: datetime | None = None
1215

1316

1417
class ReviewRequestResponse(BaseModel):

services/reviewhelper-api/app/tasks.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from datetime import datetime
23
from functools import cache
34

45
from google.cloud.tasks_v2 import (
@@ -19,11 +20,14 @@ def _get_tasks_client():
1920
return CloudTasksAsyncClient()
2021

2122

22-
async def create_review_task(review_request_id: int):
23+
async def create_review_task(
24+
review_request_id: int, schedule_time: datetime | None = None
25+
):
2326
"""Create a Cloud Task to process a review request.
2427
2528
Args:
2629
review_request_id: The ID of the review request to process.
30+
schedule_time: Optional time to delay task dispatch until.
2731
"""
2832
client = _get_tasks_client()
2933

@@ -44,6 +48,7 @@ async def create_review_task(review_request_id: int):
4448
},
4549
),
4650
dispatch_deadline=Duration(seconds=30 * 60),
51+
schedule_time=schedule_time,
4752
)
4853

4954
response = await client.create_task(parent=parent, task=task)

0 commit comments

Comments
 (0)