If your app doesn't share credentials across servers, processes, and threads, it may be sending an excessive number of requests to Google. This can cause our servers to enforce rate limits on your app, resulting in performance degradation.
This section explains how to optimize OAuth2 credential management so your app can efficiently interact with the Google Ads API.
Credential sharing strategies
Sharing credentials across your API requests improves performance and avoids excessive overhead that can result in rate limit errors.
Your credential sharing strategy depends on your app's design:
- Multithreaded apps share a thread-safe credential.
- Multiprocess or distributed apps share a credential via a shared data store.
In multithreaded apps, you should supply each thread's session with the same credential.
In multiprocess or distributed apps, you must implement some infrastructure to share the credential across processes. You should also ensure that threads aren't blocked, and that race conditions aren't present in your implementation.
An app that is both multiprocess / distributed and multithreaded within each process should use both strategies.
These strategies are described below for authenticating a single AdWords account, such as the top-level manager account in your hierarchy.
Adapting these strategies to authenticate for multiple AdWords accounts is described later in this document.
In multithreaded apps, the credential should be shared between threads. Refreshing of the credential should be performed synchronously to avoid a race condition.
The diagram shows a runtime with threads drawing from a pool of sessions (or users) to make requests to the Google Ads API. Note that each session should use the same credential object. On each API request, the thread obtains a session (or user). If the credential requires an access token refresh, it must be done synchronously--the credential object must be thread-safe so as to avoid a race condition.
The client libraries make sharing a credential across threads straightforward. Each client library has a session (or user) object with a credential that it reuses throughout its lifetime. To share the credential across threads, simply construct each session using the same credential. In all client libraries, the credential is a thread-safe object that refreshes itself synchronously when its access token expires.
For example, in the Java client library, you would create a
Credential as a singleton and share it across all sessions.
Multiprocess or distributed apps
In multiprocess or distributed apps, a shared credential should be used. To ensure multiple processes or servers don't attempt to refresh the credential at the same time (resulting in excessive refresh requests), we advise proactively refreshing the credential in a central place and sharing it across processes/servers.
For example, a separate job or service can be responsible for periodically refreshing the credential, and proactively pushing it to the data store for use by the pool of servers.
The diagram above shows the credential refresh job running periodically and writing the credential's properties to the data store. Each server then retrieves the credential before making a request to the API.
The refresh job periodically refreshes the credential and stores it in the data store. The job shouldn't wait until the current credential expires before initiating a refresh: doing so would open a window when the app is stalled for lack of a valid credential.
A better alternative is to force the refresh periodically, each time replacing the credential in the data store with the new one. The refresh job should run well before the current credential expires to allow some time in case of a transient failure. Every 15 minutes is a good starting point.
A central data store can be used to share the credential between processes and servers.
You can make use of an existing data store or deploy one specific to the sharing of credentials between servers. Solutions include caching servers (such as Memcached or Infinispan), or NoSQL data stores (such as MongoDB).
The data store must provide a reliable interface to all servers making requests to the API. It should be optimized for fast reading operations, since the server or process will read the current credential more frequently than the refresh job will update it.
Keep in mind that credentials must be stored securely.
When storing the credential, you should store the calculated
refresh_token alongside the
expiry_time is calculated as the time of the
access_token refresh request
Each server or process in the pool retrieves the latest credential from the data store before making a request. As long as the refresh job is running successfully, the credential will be valid. But if the refresh job or data store fails, you should have a fallback mechanism.
If a server or process cannot get a credential from the data store, or if the credential is expired, the server should refresh its own credentials to allow the app to continue working with the API until the problem is resolved.
In processes with multiple threads, you should use the same sharing strategy described above to share the credential across threads.
Authenticating multiple accounts
A credential generated for an AdWords manager account can be used to access all its child accounts. Thus, for users with a single manager account hierarchy, it's usually sufficient to generate a credential for the top-level manager account, thereby authorizing the app for all the AdWords accounts beneath it.
In other cases, your app must access AdWords accounts that are not related to one another in any manager account hierarchy. In this case, you should generate and maintain different credentials for different accounts, such as for each AdWords client account you access, or each top-level manager account in the independent hierarchies you access.
You can follow the same strategies for multithreaded
and multiprocess / distributed
apps with minor modification. When using a shared data store,
credentials must be indexed by the account identifier
ensure credentials are associated with the right account.
Additionally, the refresh job should keep all credentials
refreshed. If a new account is linked, the refresh job may need to be triggered.
Finally, in multithreaded apps, you should only share the credential object across threads that are operating on the account with which the credential object is associated.
- You can get more familiar with OAuth2 internals for the Google Ads API, or start using the OAuth Playground to generate refresh and access tokens.