Skip to content

Conversation

@bobhancockg
Copy link
Contributor

add_async_campaign.py
Batch Processing:
- Replaced CampaignBudgetServiceClient and CampaignServiceClient with the unified GoogleAdsService.
- Implemented MutateOperation for both budget and campaign.
- Used temporary resource IDs (e.g., customers/{customer_id}/campaignBudgets/-1) to allow the campaign to reference the budget created in the same request.
- Executed both operations in a single mutate call.

async_search_stream.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.`

sync_search.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.

In add_async_campaign.py I replaced the individual service clients with the unified GoogleAdsService to enable atomic batch processing using Temporary Resource Names.

Here is the breakdown of why this was necessary for the optimization:

Single Network Request: The GoogleAdsService.mutate method allows you to bundle operations for different resource types (simultaneously creating a CampaignBudget and a Campaign) into one single API call. The individual clients (CampaignBudgetServiceClient, etc.) can only handle their specific resource type, forcing you to make sequential network requests.

Temporary Resource Names: This is the key feature that makes 1-step creation possible. By using a temporary ID (like customers/123/campaignBudgets/-1), I could tell the API: "Create this budget, and simultaneously create this campaign that references that budget you are about to create."

If I used the individual services, I would have to await the budget creation, get the real resource name back, and then start the campaign creation request (2 round-trips).

bobhancock and others added 5 commits December 6, 2025 20:47
:
Verifies that the
mutate
 method is called exactly once with two operations (budget and campaign).
Checks for the correct usage of temporary resource names.
tests/examples/asyncio/async_search_stream_test.py
:
Verifies that search_stream is called with a query that does not contain LIMIT 10.
Mocks the async streaming iterator to ensure robust execution.
tests/examples/asyncio/async_search_test.py
:
Verifies that search is called with a query that does not contain LIMIT 10.
Here is the list of modifications I have made during this session:

Optimized Files
examples/asyncio/async_add_campaigns.py
:
Optimization: Implemented batch processing using GoogleAdsService.mutate and temporary resource IDs.
Benefit: Reduced network round trips from 2 to 1 and ensured atomic creation of Budget and Campaign.
examples/asyncio/async_search_stream.py
:
Optimization: Removed LIMIT 10 from the GAQL query.
Benefit: Aligned code behavior with its description ("get all campaigns") and correctly demonstrates streaming.
examples/asyncio/async_search.py
:
Optimization: Removed LIMIT 10 from the GAQL query.
Benefit: Aligned code behavior with its description ("get all campaigns").
New Unit Tests
tests/examples/asyncio/async_add_campaigns_test.py
- Replaced CampaignBudgetServiceClient and CampaignServiceClient with the unified GoogleAdsService.
- Implemented MutateOperation for both budget and campaign.
- Used temporary resource IDs (e.g., customers/{customer_id}/campaignBudgets/-1) to allow the campaign to reference the budget created in the same request.
- Executed both operations in a single mutate call.

async_search_stream.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.`

sync_search.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.

New Unit Tests
tests/examples/asyncio/async_add_campaigns_test.py
- Replaced CampaignBudgetServiceClient and CampaignServiceClient with the unified GoogleAdsService.
- Implemented MutateOperation for both budget and campaign.
- Used temporary resource IDs (e.g., customers/{customer_id}/campaignBudgets/-1) to allow the campaign to reference the budget created in the same request.
- Executed both operations in a single mutate call.

async_search_stream.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.`

sync_search.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.

Merge branch 'opt_asyncio' of https://github.com/googleads/google-ads-python into opt_asyncio
Batch Processing:
- Replaced CampaignBudgetServiceClient and CampaignServiceClient with the unified GoogleAdsService.
- Implemented MutateOperation for both budget and campaign.
- Used temporary resource IDs (e.g., customers/{customer_id}/campaignBudgets/-1) to allow the campaign to reference the budget created in the same request.
- Executed both operations in a single mutate call.

async_search_stream.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.`

sync_search.py
Query Optimization:
- Removed LIMIT 10 from the GAQL query to correctly illustrate "getting all campaigns" as per the example's purpose.

In add_async_campaign.py I replaced the individual service clients with the unified
GoogleAdsService to enable atomic batch processing using Temporary Resource Names.

Here is the breakdown of why this was necessary for the optimization:

Single Network Request: The GoogleAdsService.mutate method allows you to bundle operations for different resource types (simultaneously creating a CampaignBudget and a
Campaign) into one single API call. The individual clients (CampaignBudgetServiceClient, etc.) can only handle their specific resource type, forcing you to make sequential network requests.

Temporary Resource Names: This is the key feature that makes 1-step creation possible. By using a temporary ID (like customers/123/campaignBudgets/-1), I could tell the API: "Create this budget, and simultaneously create this campaign that references that budget you are about to create."
If I used the individual services, I would have to await the budget creation, get the real resource name back, and then start the campaign creation request (2 round-trips).

Atomicity: This approach ensures that either both resources are created successfully, or neither is. If the campaign creation fails, the budget won't be left orphaned in your account.
@bobhancockg bobhancockg requested a review from a team as a code owner December 7, 2025 03:18
@bobhancockg bobhancockg requested review from BenRKarl and laurachevalier4 and removed request for laurachevalier4 December 7, 2025 03:18
@BenRKarl BenRKarl added the kokoro:run This label is required to run a kokoro presubmit if an external contributor sends a PR. label Dec 8, 2025
@kokoro-team kokoro-team removed the kokoro:run This label is required to run a kokoro presubmit if an external contributor sends a PR. label Dec 8, 2025
BenRKarl
BenRKarl previously approved these changes Dec 8, 2025
# We are creating both the budget and the campaign in the same request, so
# we need to use a temporary resource name for the budget to reference it
# in the campaign.
# Temporary resource names must be negative integers formatted as strings.
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added the link and pushed the change.

@BenRKarl BenRKarl added the kokoro:run This label is required to run a kokoro presubmit if an external contributor sends a PR. label Dec 9, 2025
@kokoro-team kokoro-team removed the kokoro:run This label is required to run a kokoro presubmit if an external contributor sends a PR. label Dec 9, 2025
@BenRKarl BenRKarl merged commit 1d26beb into main Dec 9, 2025
4 checks passed
@BenRKarl BenRKarl deleted the opt_asyncio branch December 9, 2025 14:20
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