Every year, engineering teams face high-stakes peak traffic events: Black Friday / Cyber Monday (BFCM), Diwali sales, Boxing Day, major product launches, and seasonal marketing surges. When stateless frontend services experience traffic spikes, autoscalers can spin up instances to absorb the load. Stateful databases do not have that luxury (except for Enterprise Plus readpool autoscaling). If your primary database exhausts its available connections, hits memory exhaustion, or locks up during a background maintenance reboot, your entire application goes down.
Over years of supporting enterprise databases on Google Cloud, we have observed that the vast majority of event outages are preventable. They rarely stem from engine-level bugs. They are caused by missing maintenance deny windows, inadequate connection buffering, disabled Point-in-Time Recovery, or simply no compute headroom.
In this guide, we walk through a 13-point Cloud SQL Event Readiness Audit using standard gcloud CLI commands and lightweight Cloud Monitoring REST API calls via curl. These are infrastructure-layer checks: things you can verify from outside the database, without a SQL connection to database, using only read permissions on sqladmin.googleapis.com. You can run them directly in Cloud Shell or your local terminal to validate your database fleet before your next peak milestone.

The Golden Rules of Event Readiness
Before running individual commands, establish these four operational guardrails:
- Enforce a Strict Configuration Freeze. Freeze all schema migrations, database flag changes, and connection logic 2 to 3 weeks before the event. Only critical, pre-tested emergency fixes should be permitted.
- Buffer for Spikes. Under peak load, your baseline utilization should not exceed 60% for CPU and 70% for Memory. Peak events consistently introduce unforeseen query spikes and analytical reporting loads.
- Verify Blast Radius. Ensure every production primary has regional high availability and a tested disaster recovery strategy.
- Pre-Plan Your Scale-Ups, and Talk to Your Account Team Early. Decide before the freeze what your scaled configuration looks like, and apply it before the freeze. On Enterprise, a tier change restarts the instance, so apply your scale-up before the freeze. On Enterprise Plus, scaling is a sub-second operation, so the constraint is capacity and quota rather than downtime. Size for the peak you forecast, apply it early, and load test the configuration you will actually run. Then engage your Google Cloud account team well ahead of the event with your projected footprint: regions, machine shapes, replica counts, and expected connection volume. They are the right path for quota increases and for capacity questions that you cannot answer from the Console alone. Quotas are per project and raising them is a process with a lead time, not a switch. If you do not have an account team, open a support case early rather than during the event.
Review below public documents to help yourself prepare for a peak capacity event
- Prepare for a peak capacity event | Cloud Customer Care | Google Cloud Documentation
- Event readiness services | Cloud Customer Care | Google Cloud Documentation
- Premium Support overview | Cloud Customer Care | Google Cloud Documentation
What This Audit Deliberately Does Not Check, and Why
An audit is only trustworthy if it is honest about its own edges. Thirteen green checks is a meaningful statement, but only if you know what the thirteen do not cover. Every exclusion below is deliberate, and each one falls into one of 3 categories.
The rule that produces these exclusions is simple: a check earns its place only if it is edition-neutral, verifiable without a database connection, and capable of failing for a reason that would actually hurt you during the event. Anything that misses one of those three is excluded, and named here rather than quietly omitted.
Group 1: Inside the database
These require a SQL connection and knowledge of your schema. The audit runs entirely against the Admin API and Cloud Monitoring, so it cannot see them, and a platform team running the audit across someone else’s fleet should not be inside those databases anyway.

On that last row, one warning is worth repeating. Do not change critical database flags close to the event. Flags such as sync_binlog and innodb_buffer_pool_size for MySQL, or toggling autovacuum in PostgreSQL, can compromise the stability of the instance or the durability of its data. The audit does not grade your flags, and it also cannot protect you from changing them at the wrong moment.
While Google manages the physical hardware, host operating system, and automated replication mechanisms, the customer remains strictly responsible for schema indexing, query optimization, connection pooling architectures, flag modifications, and workload capacity sizing. Please review Cloud SQL Shared Responsibility Model.
Group 2: Enterprise Plus capabilities
Cloud SQL ships in two editions, and the Admin API reports which one an instance uses. Enterprise Plus surfaces as ENTERPRISE_PLUS. A number of capabilities exist only on that edition, and none of them appears as a check in this audit. That is deliberate. Grading an Enterprise instance on something it cannot enable produces a finding that no configuration change can fix, only a purchase order. That is a sales conversation wearing a readiness report's clothing, and it is not what this audit is for.
But the exclusions are not all alike, and it is worth being precise about the difference.
The capabilities we simply do not look at
These are real, useful, and entirely absent from the thirteen checks. If you run Enterprise Plus, read this as an optimization backlog rather than a gap list.
- Connection and readpool scaling. Managed Connection Pooling is an Enterprise Plus capability. So are readpools, which are rejected outright on Enterprise instances. Check 9 will tell you that you are approaching your connection ceiling. It will not tell you that Enterprise Plus offers a managed way out of it.
- Caching and write throughput. Data cache is Enterprise Plus only. For MySQL specifically, optimized writes is another differentiator.
- Disaster recovery tooling. Check 12 asks only whether a cross-region replica exists. What it does not evaluate is the designated DR replica, which is an optional Enterprise Plus configuration, nor the write endpoint for advanced disaster recovery and its associated connectivity. Those are what turn a copy of your data into a rehearsed switchover.
- Diagnostics. AI-assisted troubleshooting and the enhanced recommenders are both Enterprise Plus features.
One caveat on scope: several of these are gated by engine as well as edition. Read pools and Managed Connection Pooling apply to MySQL and PostgreSQL. Optimized writes is MySQL only. SQL Server Enterprise Plus gets a memory-optimized machine series instead. “Enterprise Plus only” is rarely the whole constraint.
The differences that change what a finding means
These are not features you might switch on. They are behavioural differences that make an identical audit finding mean two different things depending on which edition you are looking at.
- Maintenance is nearly invisible on Enterprise Plus. Maintenance downtime is under a second on Enterprise Plus, against under 30 seconds for PostgreSQL, 60 for MySQL and 120 for SQL Server on Enterprise.
- Scaling is a sub-second operation on Enterprise Plus. On Enterprise Plus, the binding constraint is regional capacity and quota, not downtime.
- The SLA itself differs. Enterprise Plus carries a 99.99% availability SLA that includes maintenance; Enterprise is 99.95% and excludes it.
- Two ceilings are different numbers, not different features. PITR transaction log retention goes up to 35 days on Enterprise Plus against 7 on Enterprise, which is why Check 2 expresses its criterion relative to your event window instead of as a fixed day count. Machine sizing tops out at 128 vCPU and 864 GB on Enterprise Plus versus 96 vCPU and 624 GB on Enterprise. Query Insights retains 30 days of metrics on Enterprise Plus against 7 on Enterprise, with a longer query string limit, more plan samples, and index advisor recommendations. If you are planning post-event analysis, a 7 day window closes fast after a long holiday weekend. Decide before the event, not after.
Group 3: Outside the database entirely
These matter as much as anything in the checklist, and none of them is visible to an API call against a database instance.
- Application behaviour during failover: Whether your connection pool reconnects cleanly, whether writes are idempotent, whether retries back off. Verify this by testing a failover, not by reading configuration.
- Client-side pool sizing: The instance reports how many connections arrived, never how many your pods were configured to open.
- Load testing: Configuration audits establish structural readiness. Behaviour under 1.5× projected peak is a separate exercise, covered in the conclusion.
- Networking, IAM, and dependent services: Private Service Connect, authorized networks, Secret Manager, and the services your database talks to all have their own readiness concerns.
- DR rehearsal: Check 12 confirms a cross-region replica exists. It cannot confirm anyone has ever promoted one, or how long that took.
Step 0: Discover and Inventory Your Database Fleet
List all Cloud SQL instances in your project, capturing edition and tier as context rather than as criteria:
export PROJECT_ID="your-gcp-project-id"
gcloud sql instances list \
--project="${PROJECT_ID}" \
--format="table(name, databaseVersion, settings.edition, settings.tier,
settings.availabilityType, state, region, instanceType)"
Confirm that all production instances are in the RUNNABLE state. Record each instance's role: instanceType=CLOUD_SQL_INSTANCE is a primary, READ_REPLICA_INSTANCE is a replica. Several checks apply to one role and not the other, and getting this wrong is the fastest way to produce a report full of irrelevant findings.
Shared setup for the metrics checks
Checks 4, 7, 8, 9, and 10 query Cloud Monitoring. Define these once per shell session rather than repeating them:
export INSTANCE_NAME="your-instance-name"
export TOKEN=$(gcloud auth print-access-token)
export END_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
export START_TIME=$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)
A small helper keeps the rest of the post readable and removes hand-encoded URL parameters, which are the most fragile thing to copy out of a blog post:
query_max_metric() {
local metric_type="$1"
local extra_filter="${2:-}"
local filter="metric.type=\"${metric_type}\" AND resource.labels.database_id=\"${PROJECT_ID}:${INSTANCE_NAME}\""
[[ -n "${extra_filter}" ]] && filter="${filter} AND ${extra_filter}"
curl -s -H "Authorization: Bearer ${TOKEN}" -G \
"https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries" \
--data-urlencode "filter=${filter}" \
--data-urlencode "interval.startTime=${START_TIME}" \
--data-urlencode "interval.endTime=${END_TIME}" \
--data-urlencode "aggregation.alignmentPeriod=3600s" \
--data-urlencode "aggregation.perSeriesAligner=ALIGN_MAX" \
--data-urlencode "aggregation.crossSeriesReducer=REDUCE_MAX"
}
The aggregation parameters matter. Without ALIGN_MAX, a seven-day unaggregated query returns thousands of raw points and paginates, leaving you to compute the maximum yourself. With it, the API returns one hourly peak per bucket and the answer is readable at a glance.
Check 1: High Availability (Regional vs. Zonal)
Cloud SQL High Availability uses a regional configuration with synchronous replication between an active primary in one zone and a standby instance in a different zone within the same region. If the primary zone encounters an outage, Cloud SQL fails over automatically. Instances configured as Zonal are flagged as high risk, because a regional configuration is what backs the high availability uptime SLA.
Inspection Command
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="value(settings.availabilityType)"
- Pass: REGIONAL [HEALTHY]
- Risk: ZONAL [RISK] — a single zone failure will cause an outage, and the instance lacks HA SLA coverage.
Remediation Command
gcloud sql instances patch "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--availability-type=REGIONAL
Converting from ZONAL to REGIONAL causes a short failover and restart window. Execute this during an off-peak maintenance window well before your freeze date.
Operational Disclaimer: Remediation commands provided in this guide are illustrative. Modifying instance settings — such as altering availability types, changing machine tiers, or enabling Point-in-Time Recovery — can trigger instance restarts, failovers, or momentary drops in connectivity. Never execute mutating commands in production without testing in staging and adhering to your organization’s formal change management windows.
Check 2: Automated Backups and Point-in-Time Recovery (PITR)
Backups protect against data corruption, failed deployments, and accidental data deletion. Daily automated backups capture a full snapshot. Point-in-Time Recovery writes write-ahead logs (WAL for PostgreSQL) or binary logs (binlogs for MySQL) continuously, enabling restoration to any second within your retention window. For primary production instances, automated backups and PITR must both be enabled.
Inspection Command (Primary Instances Only)
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="table(settings.backupConfiguration.enabled,
settings.backupConfiguration.pointInTimeRecoveryEnabled,
settings.backupConfiguration.startTime,
settings.backupConfiguration.transactionLogRetentionDays)"
- Pass: enabled=True and pointInTimeRecoveryEnabled=True, with transactionLogRetentionDays spanning at least your event window plus realistic detection-and-decision time [HEALTHY]
- Risk: pointInTimeRecoveryEnabled=False [RISK] — no granular restore capability.
- Risk: enabled=False [RISK] — no disaster recovery snapshots at all.
Express the retention criterion relative to your own event window, not as a fixed number of days. The configurable maximum differs by edition: Enterprise Plus supports a longer range than Enterprise. Defaults diverge as well, with Enterprise Plus defaulting to a longer retention period on some engines. A hard “retain at least N days” threshold would therefore fail Enterprise instances for a limit they cannot exceed.
Remediation Command
gcloud sql instances patch "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--backup-start-time="02:00" \
--enable-point-in-time-recovery \
--retained-transaction-log-days=7
For MySQL, ensure binary logging is also active (–enable-bin-log).
Important timing caveat: toggling PITR is not free. Disabling and re-enabling PITR restarts the instance, and you lose the ability to recover to any point before the disablement. Treat this like the HA conversion in Check 1: do it early, deliberately, and never inside the freeze window.
Check 3: Storage Autogrow (Automatic Storage Increase)
During high-volume traffic events, table row inserts surge, temporary disk space is consumed by large sorts, and transaction logs/WAL accumulate rapidly. If a database exhausts disk space, it crashes or transitions into a read-only state. Automatic storage increase is the standard defence against downtime caused by running out of disk.
Inspection Command
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="table(settings.storageAutoResize, settings.storageAutoResizeLimit, settings.dataDiskSizeGb)"
- Pass: storageAutoResize=True [HEALTHY]. If storageAutoResizeLimit is set, confirm the limit leaves ample room above current usage. A limit set just above today's disk size gives you autogrow on paper and a full disk in practice.
- Risk: storageAutoResize=False [RISK] — the instance will lock or fail if the disk fills.
Remediation Command
gcloud sql instances patch "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--enable-storage-auto-increase
Check 4: Current Disk Utilization (7-Day Lookback)
Autogrow is a safety net, not a sizing strategy. Disk utilization consistently above 95% with automatic storage increase disabled means the disk is not properly sized.
Inspection Command
query_max_metric "cloudsql.googleapis.com/database/disk/utilization"
- Pass: Max utilization < 0.80 (80%) [HEALTHY]
- Warning: 0.80 to 0.95 [WARNING] — acceptable only with autogrow enabled and a generous limit.
- Risk: > 0.95 (95%) [RISK] — the disk is undersized for the current workload, before the event has even started.
Check 5: Maintenance Version Currency
Cloud SQL periodically releases maintenance updates containing security patches and engine stability fixes. This check precedes the deny period check deliberately: the two are coupled, and version currency is the prerequisite.
Inspection Command
gcloud sql instances list \
--project="${PROJECT_ID}" \
--filter="name=${INSTANCE_NAME}" \
--format="table(name, maintenanceVersion, availableMaintenanceVersions)"
- Pass: availableMaintenanceVersions is empty [HEALTHY]
- Warning: a newer maintenance version is available [WARNING] — schedule a self-service upgrade during an off-peak window well before the freeze.
- Risk: the instance is running a maintenance version more than 12 months old [RISK] — see Check 6. This is not merely hygiene; it blocks your ability to protect the event at all.
Check 6: Maintenance Deny Periods
GCP regularly executes automated maintenance to deploy hypervisor, OS, and database security fixes. While critical for long-term health, automated maintenance must not trigger reboots during your event window. A deny maintenance period is the supported way to block maintenance during critical business hours.
Four constraints are worth understanding before you rely on this:
- A deny maintenance period blocks maintenance for up to 90 consecutive days at a time.
- An instance whose maintenance version is older than 12 months cannot have a deny period set at all, which is exactly why Check 5 comes first.
- Read replicas observe the deny maintenance period configured on their corresponding primary, so you set this once on the primary rather than per replica.
Inspection Command (Primary Only)
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="json(settings.maintenanceWindow, settings.denyMaintenancePeriods)"
- Pass: an active deny window fully covering your event start and end dates [HEALTHY]
- Risk: no deny window configured, or a window that expires before the event ends [RISK]
Remediation Command
# Times are set in UTC. 00:00:00 UTC corresponds to 16:00:00 PST / 17:00:00 PDT.
gcloud sql instances patch "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--deny-maintenance-period-start-date="2026-11-15" \
--deny-maintenance-period-end-date="2026-12-05" \
--deny-maintenance-period-time="00:00:00"
Watch the timezone conversion carefully here. A deny window that looks correct in UTC can start hours after your event begins in local time.
A deny maintenance period does not prevent all downtime. Instances can still be affected by infrastructure issues or by changes you make yourself. It removes one predictable class of risk. It is not an availability guarantee.
If the command fails with an unrecognised-option error, update to the latest version of the Cloud CLI before assuming the feature is unavailable.
Check 7: Maximum CPU Utilization (7-Day Lookback)
Checking historical CPU utilization ensures your database has adequate headroom to absorb traffic spikes without query queuing.
Inspection Command
query_max_metric "cloudsql.googleapis.com/database/cpu/utilization"
- Pass: Max utilization < 0.60 (60%) [HEALTHY]
- Warning: 0.60 to 0.80 [WARNING]
- Risk: > 0.80 (80%) [RISK] — the instance may throttle or queue during peak events.
Be clear with your stakeholders about which number you are quoting. The published operational guideline is that CPU utilization should stay below 90%, and an instance consistently above that is considered improperly sized. The 60% threshold above is a stricter event planning target, deliberately conservative to leave room for the spike you did not forecast. Both are useful. Conflating them is how you end up arguing about whether 80% CPU is “supported.”
Remediation Command
# Example: scale up to 16 vCPUs and 64 GB RAM on an Enterprise edition instance.
# Use the tier family appropriate to the instance's edition.
gcloud sql instances patch "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--tier="db-custom-16-65536"
Check the instance’s edition before composing this command. Tiers are validated per edition, so an Enterprise tier string will be rejected on an Enterprise Plus instance and vice versa.
Check 8: Maximum Memory Utilization (7-Day Lookback)
High memory utilization puts the database at risk of kernel Out-of-Memory kills, which trigger abrupt database reboots. Memory is the metric people most often misread, so it is worth being precise about which series you query.
Inspection Command
# Primary signal for OOM exposure
query_max_metric "cloudsql.googleapis.com/database/memory/components" \
'metric.labels.component="usage"'
# Secondary, VM-level view
query_max_metric "cloudsql.googleapis.com/database/memory/utilization"
- Pass: components.usage max < 0.70 (70%) [HEALTHY]
- Warning: 0.70 to 0.90 [WARNING]
- Risk: > 0.90 (90%) [RISK] — a value above 90% for this component is a clear signal to provision a larger instance.
A note on which memory metric to trust
Cloud SQL exposes several memory series and they do not mean the same thing:
- database/memory/components.usage is the actionable one for OOM risk. The published operational guideline likewise treats memory consistently over 90% as an improperly sized instance.
- database/memory/utilization is VM-level usage divided by quota. Useful context, but it is not the component breakdown.
- database/memory/total_usage, shown as "Total memory usage" in Cloud Monitoring, includes the OS page cache and will sit near 100% for long stretches purely because of how the OS uses page cache.
If your dashboard shows “Total memory usage” pinned near 100%, do not scale the instance on that basis alone. Look at the component breakdown first and check whether the consumption is cache rather than genuine usage.
Check 9: Maximum Connected Clients (7-Day Lookback)
When application pods autoscale during an event, each new worker opens database connections. If total connections approach your database limit, new connection attempts fail with connection refused errors. The operational guideline is to keep active connections below 80% of your configured maximum.
This criterion is self-normalizing: it compares each instance against its own configured limit, so it behaves identically regardless of edition.
Inspection Command
# MySQL / SQL Server
query_max_metric "cloudsql.googleapis.com/database/network/connections"
# PostgreSQL
query_max_metric "cloudsql.googleapis.com/database/postgresql/num_backends"
- Pass: max connections < 80% of the instance limit [HEALTHY]
- Warning: 80% to 90% [WARNING]
- Risk: > 90% [RISK] — connection refused errors are likely during peak.
Reserve headroom beyond your application’s needs. Administrative sessions, health checks, and your own emergency diagnostic connections all need somewhere to go, and they need it most precisely when the pool is saturated.
Remediation Strategy
- Reduce per-pod application pool sizing so that horizontal pod scaling does not multiply into connection flooding. A pool of 20 per pod across 50 pods is 1000 connections.
- Deploy client-side or sidecar pooling. PgBouncer for PostgreSQL and ProxySQL for MySQL work on both editions.
- Enterprise Plus only: Cloud SQL Managed Connection Pooling.
Check 10: Read Replica Replication Lag (7-Day Lookback)
Read Replicas offload read queries from the primary. Under heavy write throughput or with under-provisioned replica resources, replication lag builds up. Replicas with high lag serve stale data and risk falling out of sync.
Inspection Command (Read Replicas Only)
query_max_metric "cloudsql.googleapis.com/database/replication/replica_lag"
- Pass: sustained lag < 10 seconds [HEALTHY]
- Warning: sustained lag between 10 and 60 seconds [WARNING]
- Risk: sustained lag > 60 seconds [RISK]
You can adjust the replication lag thresholds for check as per your requirements. I am treating 10 sec lag as acceptable, value might not be acceptable for your use case
Remediation Strategy
- Size the read replica’s CPU and memory to match or exceed the primary. A replica smaller than its primary is a lag generator by construction.
- For MySQL, enable multi-threaded replication via replica_parallel_workers or slave_parallel_workers. This is a pre-freeze change, per the flag guidance above.
Check 11: Query Insights and Observability
During a live event, running heavy ad-hoc diagnostic queries adds unnecessary load. Query Insights provides lightweight, continuous query performance tracking, wait events, and execution plan visibility. It is also the tool that covers the schema and query layer this audit deliberately does not.
Inspection Command
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="value(settings.insightsConfig.queryInsightsEnabled)"
- Pass: True [HEALTHY]
- Warning: False or empty [WARNING] — limited diagnostic visibility during incidents.
Evaluate the base boolean and nothing else. Some advanced Query Insights capabilities, such as blocked and blocking statistics in active queries, are available only on Enterprise Plus. Grading an Enterprise instance against those would penalise it for a feature it cannot enable.
Remediation Command
gcloud sql instances patch "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--insights-config-query-insights-enabled \
--insights-config-record-application-tags \
--insights-config-record-client-address \
--insights-config-query-string-length=1024
Enable this well before the event. Observability you switch on during an incident gives you data starting from the incident, with no baseline to compare against.
Query Insights retention is 7 days on Enterprise. If a customer plans to do post-event analysis, a 7 day window closes fast after a long weekend sale, and that is a decision to make before the event rather than after.
Check 12: Cross-Region Disaster Recovery
For mission-critical revenue pathways, regional high availability only protects against a single zone failure within a region. A full regional disruption requires cross-region replication for continuity.
Inspection Command (Primary Instances Only)
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="json(name, region, replicaNames)"
- Pass: a read replica exists in a different geographic region [HEALTHY]
- Risk: no cross-region replica exists [RISK] — a regional incident results in total service downtime.
Judge only on the existence of a cross-region replica. Deliberately not part of the criterion is the designated DR replica, which is an Enterprise Plus configuration.
Existence is the easy half. The harder question, and the one worth answering before the event rather than during it, is whether anyone has rehearsed the promotion: who decides, how long promotion takes, what the application does with its connection strings, and what data loss is acceptable. An untested DR replica is a line item, not a plan.
Check 13: Regional Quotas and Cloud SQL Admin API Throttling
Emergency scaling can fail if your project lacks regional resource quota or if control-plane API rate limits are exceeded. Reviewing quotas in the Cloud Console (IAM & Admin > Quotas & System Limits) avoids specialized API authentication overhead.
Audit Cloud SQL Admin API Rate Throttling
Automated scripts, CI/CD pipelines, and monitoring pollers can exhaust the Cloud SQL Admin API. Check Cloud Logging for recent RESOURCE_EXHAUSTED errors:
gcloud logging read \
'protoPayload.serviceName="sqladmin.googleapis.com" AND protoPayload.status.code=8' \
--project="${PROJECT_ID}" \
--freshness="1d" \
--format="table(timestamp, protoPayload.methodName, protoPayload.status.message)"
- Pass: zero throttling errors and regional CPU headroom > 30% [HEALTHY]
- Warning: regional CPU headroom between 10% and 30% [WARNING]
- Risk: active rate limit errors or regional CPU headroom < 10% [RISK]
Interpret an empty result carefully. “No rows returned” can mean “no throttling occurred” or it can mean “the relevant audit logs are not being captured in this project.” Confirm your audit log configuration before recording this as a pass.
Event Readiness Summary Matrix
Note that edition and tier appear in the inventory table, not in the pass/fail grid. They provide context for interpreting results; they are not graded.

Running This Audit Automatically with an AI Agent
Running thirteen checks by hand against one instance is a few minutes of work. Running them across forty instances in six projects, three weeks before BFCM, is an afternoon you do not have. The checklist below is also published as an Agent Skill, a plain-Markdown SKILL.md file that teaches a coding agent the commands, the pass and fail thresholds, and the report format. You point the agent at a project, and it handles the enumeration, the API calls, the threshold comparison, and the write-up.
Agent Skills follow a portable, open format: a skill is simply a directory containing a SKILL.md file with YAML frontmatter and Markdown instructions, which the agent loads on demand when your request matches the skill's description. Optional scripts/, references/, and resources/ subdirectories can accompany it, and the agent explores them only when needed. The same file works across agents that support the standard.
Prerequisites (both tools)
gcloud auth login
gcloud auth application-default login
gcloud config set project [PROJECT_ID]
# Confirm you can read instances and metrics before involving the agent
gcloud sql instances list --project="[PROJECT_ID]"
The agent runs the same gcloud and curl commands you would. Grant it nothing more than roles/cloudsql.viewer and roles/monitoring.viewer for the audit pass. Keep roles/cloudsql.admin out of the loop entirely, and apply any remediation yourself through your normal change process.
Option A: Antigravity
- Create the skill directory and save the SKILL.md from this post into it:
mkdir -p ~/.gemini/skills/cloudsql-peak-event-audit
# save SKILL.md into that directory
Antigravity manages agent skills under ~/.gemini, and its skills installer can target either a global location or your current workspace. Confirm the exact path for your installed build, since this has changed between versions.
- The name in the YAML frontmatter must be lowercase with hyphens. The description matters more than you would expect: it is what the agent's planner evaluates when deciding whether to activate the skill. A vague description means the skill never fires.
- Restart Antigravity so the skill is discovered, then confirm it is registered by typing / in the chat input. Skills appear in the slash-command menu and can be invoked directly.
- Run the audit in natural language:
Use the cloudsql-peak-event-audit skill. Audit every Cloud SQL instance in
project [PROJECT_ID] for our BFCM event running YYYY-MM-DD to YYYY-MM-DD.
Produce the findings report, and list remediation commands separately.
Do not execute any patch commands.
If you are iterating on the skill text itself, disable the automatic skills sync setting first, or the extension will overwrite your edits on the next reload.
Option B: Gemini CLI
- Gemini CLI discovers skills as subdirectories of ~/.gemini/skills/, each containing a SKILL.md:
mkdir -p ~/.gemini/skills/cloudsql-peak-event-audit
# save SKILL.md into that directory
If the skill lives in a shared repository, link it rather than copying, so you always run the latest version:
gemini skills link /path/to/repo/skills/cloudsql-peak-event-audit
- Start the CLI and verify the skill loaded:
/skills list
- Run the audit with the same prompt shown above.
Getting a useful result
Four things separate a good agent run from a noisy one:
- Give it the event dates. Check 6 is meaningless without them. The agent cannot judge whether a deny period covers your event if it does not know when your event is. Specify the timezone too, since deny periods are configured in UTC.
- Tell it what not to do. State explicitly that it must not run gcloud sql instances patch. Auditing and remediating are different change-management conversations, and they should stay that way.
- Ask it to record editions. Edition context changes which remediation is valid, even though it never changes a pass/fail threshold.
- Spot-check the metric conclusions. The Monitoring API returns time series; the agent still has to interpret them. Verify two or three instances by hand in Metrics Explorer before you trust the whole matrix.
Treat the output as a reviewed first draft, not a signed-off audit. The agent is fast and consistent at enumeration. You remain accountable for the judgement.
Conclusion: Test with Realistic Load
Configuration audits provide the structural foundation for stability, but verification requires testing under load. Once your configurations are verified and your maintenance deny window is active, run synthetic load tests against a staging or read-replica environment at 1.5x your projected peak volume. Verify application reconnection behaviour during failovers, observe replication lag under write bursts, and use Query Insights to identify and optimize query bottlenecks well in advance of peak traffic.
And remember the three boundaries drawn at the top of this guide. Thirteen green checks mean the platform is ready. They do not mean the schema is indexed correctly, that autovacuum is keeping up, or that your slowest query degrades gracefully at 3x concurrency. They also do not mean you have exhausted what your edition offers: an Enterprise Plus fleet has capabilities this audit deliberately does not grade, precisely so that the same report is fair to both editions.
Appendix: SKILL.md
Update the threshold values to match your operational requirements. This is a baseline template, and you may want to add more checks tailored to your workload.
---
name: cloudsql-peak-event-audit
description: >-
Audits Cloud SQL instances (MySQL, PostgreSQL, SQL Server) ahead of peak
traffic events using gcloud, the Cloud Monitoring API, and Cloud Logging.
Read-only, infrastructure-layer only. Use when asked to assess Cloud SQL
before BFCM, Diwali, Boxing Day, a product launch, or any peak event.
metadata:
version: "4.0.0"
---
# Cloud SQL Event Readiness Audit Skill
Audit Google Cloud SQL databases for high availability, backup durability, storage
headroom, compute sizing, maintenance protection, and connection safety ahead of
major traffic events.
## Scope and Explicit Exclusions
This skill audits infrastructure and control-plane configuration only, using the
Cloud SQL Admin API, Cloud Monitoring, and Cloud Logging. It never connects to a
database.
A check earns a place here only if it is edition-neutral, verifiable without a SQL
connection, and capable of failing for a reason that would actually hurt during the
event. Everything that misses one of those three is listed below and must be named
in the report rather than silently omitted.
NOT CHECKED - inside the database:
index health, table and index bloat, autovacuum and vacuum settings,
transaction ID wraparound, buffer pool sizing, table statistics,
database flag values, query plans, lock and deadlock analysis.
Reason: requires a SQL connection and schema ownership.
NOT CHECKED - Enterprise Plus only capabilities:
managed connection pooling, read pools, data cache, designated DR replicas,
write endpoint and write endpoint connectivity, optimized writes (MySQL),
AI-assisted troubleshooting, enhanced recommenders, and the extended Query
Insights limits (longer retention, longer query strings, more plan samples,
index advisor).
Reason: an Enterprise instance cannot enable these, so grading them would
produce a finding that no configuration change can resolve.
Note: several are gated by engine as well as edition. Read pools and managed
connection pooling apply to MySQL and PostgreSQL. Optimized writes is MySQL
only. Never state "Enterprise Plus only" without checking engine scope.
NOT GRADED - recorded as inventory context only:
machine tier, edition, PITR retention as an absolute day count.
Reason: tier strings and retention maximums are edition-scoped. Actual sizing
risk is covered by the disk, CPU, memory and connection checks.
NOT CHECKED - outside the database:
application failover behaviour, client-side pool sizing, load testing,
networking and IAM configuration, DR promotion rehearsal.
Reason: not visible to the Cloud SQL Admin API or Cloud Monitoring.
NOT CHECKED - planning and human process:
pre-planned scale-up targets, capacity reservations, filed quota increase
requests, account team or support engagement, load test completion.
Reason: no API surface. These are human actions with lead times measured in
weeks. Emit the standing checklist in section 4 on every run.
State these exclusions in every report, including a fully green one. A silent
omission reads as a pass.
## Edition Handling (mandatory)
Grading is edition-neutral. Every check must use the same command, the same pass
criterion, and the same failure mode on Enterprise and Enterprise Plus. Never emit
RISK or WARNING for a capability an instance's edition does not support.
Edition still affects two things, and both belong in the report:
1. Remediation wording. Tier strings, pooling options and DR tooling differ by
edition. Read `settings.edition` before composing any suggested command.
2. Severity narrative. An identical finding does not carry identical blast radius.
Where an instance is ENTERPRISE_PLUS, annotate deny-period and scaling findings
to note that maintenance and planned operations complete with sub-second
downtime on that edition, so the impact is lower than the same finding on
Enterprise. Annotate only. Do not change the pass/fail result.
## Safety
- Audit mode is READ-ONLY. Never execute `gcloud sql instances patch` or any other
mutating command.
- Output remediation commands as text for human review only.
- Required IAM: `roles/cloudsql.viewer`, `roles/monitoring.viewer`, and read access
to Cloud Logging for the throttling check.
## Required Inputs
Collect these before starting. If any are missing, ask the user.
- `[PROJECT_ID]` - target GCP project
- `[EVENT_START_DATE]` - event start (YYYY-MM-DD)
- `[EVENT_END_DATE]` - event end (YYYY-MM-DD)
- `[EVENT_TIMEZONE]` - timezone the event dates are expressed in, for example
America/Los_Angeles. Deny maintenance periods are configured in UTC, so the
conversion must be explicit.
## Prerequisites
- Google Cloud SDK (`gcloud`) installed and authenticated.
- `gcloud config set project [PROJECT_ID]`
## 1. Discovery and Inventory
```bash
gcloud sql instances list \
--project="[PROJECT_ID]" \
--format="table(name, databaseVersion, settings.edition, settings.tier,
settings.availabilityType, state, region, instanceType)"
```
Record each instance's role. `instanceType=CLOUD_SQL_INSTANCE` is a primary and
`READ_REPLICA_INSTANCE` is a replica. Several checks apply to only one role, and
misclassifying an instance is the fastest way to fill a report with irrelevant
findings.
Edition and tier are INVENTORY ATTRIBUTES. Record them, do not grade them.
## 2. Shared Metrics Setup
Run once per session before Checks 3.4, 3.7, 3.8, 3.9, and 3.10.
```bash
export PROJECT_ID="[PROJECT_ID]"
export INSTANCE_NAME="[INSTANCE_NAME]"
export TOKEN=$(gcloud auth print-access-token)
export END_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
export START_TIME=$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)
query_max_metric() {
local metric_type="$1"
local extra_filter="${2:-}"
local filter="metric.type=\"${metric_type}\" AND resource.labels.database_id=\"${PROJECT_ID}:${INSTANCE_NAME}\""
[[ -n "${extra_filter}" ]] && filter="${filter} AND ${extra_filter}"
curl -s -H "Authorization: Bearer ${TOKEN}" -G \
"https://monitoring.googleapis.com/v3/projects/${PROJECT_ID}/timeSeries" \
--data-urlencode "filter=${filter}" \
--data-urlencode "interval.startTime=${START_TIME}" \
--data-urlencode "interval.endTime=${END_TIME}" \
--data-urlencode "aggregation.alignmentPeriod=3600s" \
--data-urlencode "aggregation.perSeriesAligner=ALIGN_MAX" \
--data-urlencode "aggregation.crossSeriesReducer=REDUCE_MAX"
}
```
Always apply the aggregation parameters. Without `ALIGN_MAX`, the API returns
thousands of raw points and paginates, and the agent is left computing the maximum
itself.
If any metric query returns an empty result, treat it as UNKNOWN and say so. Never
report an empty time series as a pass.
## 3. Technical Checks
Run all applicable checks for each discovered instance.
### Check 3.1: High Availability
*(All instances)*
```bash
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" --format="value(settings.availabilityType)"
```
- `REGIONAL`: HEALTHY
- `ZONAL`: RISK. A single zone failure causes an outage and the instance lacks HA
SLA coverage.
### Check 3.2: Automated Backups and PITR
*(Primary instances only)*
```bash
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="table(settings.backupConfiguration.enabled,
settings.backupConfiguration.pointInTimeRecoveryEnabled,
settings.backupConfiguration.startTime,
settings.backupConfiguration.transactionLogRetentionDays)"
```
- `enabled=True` and `pointInTimeRecoveryEnabled=True`: HEALTHY
- `enabled=True`, `pointInTimeRecoveryEnabled=False`: RISK
- `enabled=False`: RISK
Verify that retention covers the event window plus realistic detection and decision
time. Do NOT compare against a fixed day count. The configurable maximum is
edition-scoped.
Note in the report that disabling and re-enabling PITR restarts the instance and
forfeits recovery to any point before the disablement. Never recommend this inside
the configuration freeze.
### Check 3.3: Storage Autogrow
*(All instances)*
```bash
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="table(settings.storageAutoResize, settings.storageAutoResizeLimit, settings.dataDiskSizeGb)"
```
- `storageAutoResize=True`: HEALTHY. If `storageAutoResizeLimit` is set, verify it
leaves substantial room above current usage. A limit just above today's disk size
is autogrow on paper only.
- `storageAutoResize=False`: RISK
### Check 3.4: Disk Utilization
*(All instances)*
```bash
query_max_metric "cloudsql.googleapis.com/database/disk/utilization"
```
- Below 0.80: HEALTHY
- 0.80 to 0.95: WARNING
- Above 0.95: RISK. The disk is undersized.
### Check 3.5: Maintenance Version Currency
*(All instances. Evaluate BEFORE Check 3.6.)*
```bash
gcloud sql instances list \
--project="${PROJECT_ID}" \
--filter="name=${INSTANCE_NAME}" \
--format="table(name, maintenanceVersion, availableMaintenanceVersions)"
```
- `availableMaintenanceVersions` empty: HEALTHY
- Newer version available: WARNING. Plan a self-service upgrade before the freeze.
- Maintenance version older than 12 months: RISK. This blocks setting a deny
maintenance period entirely, so it gates Check 3.6.
### Check 3.6: Maintenance Deny Period
*(Primary instances only)*
Convert `[EVENT_START_DATE]` and `[EVENT_END_DATE]` from `[EVENT_TIMEZONE]` to UTC
before comparing. State both the local and UTC boundaries in the report.
```bash
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="json(settings.maintenanceWindow, settings.denyMaintenancePeriods)"
```
- Active deny window fully covering the event in UTC: HEALTHY
- No deny window, or a window that expires before the event ends: RISK
Constraints to state in the report:
- Deny maintenance periods can span from 1 to 90 days.
- An instance whose maintenance version is older than 12 months cannot have one set
until it is updated.
- The deny maintenance period takes precedence over a conflicting scheduled
maintenance window.
- Read replicas observe the deny maintenance period set for the primary, so do not
flag replicas separately.
- A deny period does not prevent all downtime. Instances can still be affected by
infrastructure issues or by customer-initiated changes.
For ENTERPRISE_PLUS instances, annotate this finding to note that maintenance on
that edition completes with sub-second downtime, so the impact is materially lower
than the same finding on Enterprise. Keep the RISK status unchanged.
### Check 3.7: Maximum CPU Utilization
*(All instances)*
```bash
query_max_metric "cloudsql.googleapis.com/database/cpu/utilization"
```
- Below 0.60: HEALTHY
- 0.60 to 0.80: WARNING
- Above 0.80: RISK
State clearly that 60 percent is an event planning target, and that the published
operational guidance sits at a higher threshold. Do not conflate the two.
When suggesting a tier change, read the instance's edition first. The two editions
use different machine families and the strings are not interchangeable.
### Check 3.8: Maximum Memory Utilization
*(All instances)*
```bash
# Primary signal for OOM exposure
query_max_metric "cloudsql.googleapis.com/database/memory/components" \
'metric.labels.component="usage"'
# Secondary, VM-level view
query_max_metric "cloudsql.googleapis.com/database/memory/utilization"
```
Judge primarily on the usage component:
- Below 0.70: HEALTHY
- 0.70 to 0.90: WARNING
- Above 0.90: RISK. Provision a larger instance.
The metric type is `database/memory/components` with a `component` label. There is
no metric named `components.usage`, and filtering on that string returns nothing.
The label filter is case-sensitive, so if the query returns empty, verify the label
casing in Metrics Explorer before concluding there is no data.
Do NOT judge on `database/memory/total_usage`. It includes OS page cache and sits
near 100 percent for long periods under normal operation.
### Check 3.9: Maximum Connected Clients
*(All instances)*
```bash
# MySQL / SQL Server
query_max_metric "cloudsql.googleapis.com/database/network/connections"
# PostgreSQL
query_max_metric "cloudsql.googleapis.com/database/postgresql/num_backends"
```
- Below 80 percent of the instance limit: HEALTHY
- 80 to 90 percent: WARNING
- Above 90 percent: RISK
This criterion is self-normalizing against each instance's own configured maximum,
so it is edition-neutral by construction.
Remediation for all editions: reduce per-pod pool sizing, and deploy PgBouncer or
ProxySQL. Managed connection pooling is Enterprise Plus only and applies to MySQL
and PostgreSQL. Label it explicitly as an edition-gated option.
### Check 3.10: Replication Lag
*(Read replicas only)*
```bash
query_max_metric "cloudsql.googleapis.com/database/replication/replica_lag"
```
- Below 10s: HEALTHY
- 10s to 60s: WARNING
- Above 60s: RISK
Remediation for all editions: size the replica at or above the primary, and for
MySQL enable parallel replication workers before the freeze.
### Check 3.11: Query Insights
*(All instances)*
```bash
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="value(settings.insightsConfig.queryInsightsEnabled)"
```
- `True`: HEALTHY
- `False` or empty: WARNING
Evaluate ONLY the base boolean. The extended Query Insights limits are Enterprise
Plus only and must not be graded. Where an instance is Enterprise, note in the
report that the shorter metric retention window constrains post-event analysis, and
that this is a decision to make before the event rather than after.
### Check 3.12: Cross-Region Disaster Recovery
*(Primary instances only)*
```bash
gcloud sql instances describe "${INSTANCE_NAME}" \
--project="${PROJECT_ID}" \
--format="json(name, region, replicaNames)"
```
- A replica exists in a different region: HEALTHY
- No cross-region replica: RISK. Regional HA protects only against a zone failure.
Do NOT evaluate `replicationCluster`, designated DR replica configuration, or write
endpoint setup. Those are Enterprise Plus configurations.
Add a note that existence is not rehearsal. The audit cannot confirm that anyone has
promoted the replica or measured how long it took.
### Check 3.13: Quotas and Admin API Throttling
*(Project level, once per project)*
```bash
gcloud logging read \
'protoPayload.serviceName="sqladmin.googleapis.com" AND protoPayload.status.code=8' \
--project="${PROJECT_ID}" --freshness="1d" \
--format="table(timestamp, protoPayload.methodName, protoPayload.status.message)"
```
- No throttling errors and regional CPU headroom above 30 percent: HEALTHY
- Regional headroom 10 to 30 percent: WARNING
- Active throttling or headroom below 10 percent: RISK
Caveat to include: an empty result may mean audit logs are not captured in this
project rather than that no throttling occurred.
Quota headroom is a number you read. A quota increase is a process with a lead time.
If headroom is thin, the next step is a request through the account team or quota
administrator, not a line in the report.
## 4. Findings Report Template
```
# Cloud SQL Event Readiness Audit: [PROJECT_ID]
Event window: [EVENT_START_DATE] to [EVENT_END_DATE] ([EVENT_TIMEZONE])
Equivalent UTC window: [START_UTC] to [END_UTC]
## Executive Summary
- Total Instances Checked: [N]
- Instances with Risks: [Count]
- Instances with Warnings: [Count]
- Healthy Instances: [Count]
## Instance Inventory (context, not graded)
| Instance | Role | Edition | Tier | Region | Database Version |
| :-- | :-- | :-- | :-- | :-- | :-- |
| | | | | | |
## Action Items Requiring Attention
- [INSTANCE_NAME]: [Check Name] - [RISK/WARNING] - [Issue summary]
Edition context: [only where it changes severity or remediation]
Suggested remediation (NOT executed): [command]
## Detailed Instance Audit Matrix
| Instance | Role | HA | Backups | PITR | Autogrow | Disk % | Maint Ver | Deny Period | Max CPU | Max Mem | Max Conn | Repl Lag | Insights | DR | Overall |
| :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- | :-- |
| | | | | | | | | | | | | | | | |
## Standing Human Action Items (not auditable)
- [ ] Scale-up target decided and APPLIED before the configuration freeze.
On Enterprise a tier change restarts the instance, so scaling during the
event causes downtime. On Enterprise Plus scaling is sub-second, so the
binding constraint is regional capacity and quota rather than downtime.
- [ ] Projected footprint (regions, machine shapes, replica counts, peak
connections) shared with the Google Cloud account team, or a support case
opened, well ahead of the event.
- [ ] Quota increase requests filed and approved, not merely identified.
- [ ] Load test executed against the final, post-scale-up configuration.
- [ ] Failover behaviour rehearsed, including application reconnection.
## Scope Note
Checked: infrastructure and control-plane configuration. Thirteen checks,
graded identically on Enterprise and Enterprise Plus.
Not checked and why:
- In-database (indexes, vacuum, bloat, flags, query plans): requires a SQL
connection and schema ownership. Use Query Insights and engine-native
diagnostics such as pg_stat_statements and performance_schema.
- Enterprise Plus only capabilities (managed connection pooling, read pools,
data cache, designated DR replicas, write endpoint, optimized writes,
AI-assisted troubleshooting, enhanced recommenders, extended Query Insights
limits): excluded so that Enterprise instances are not failed for features
they cannot enable. On Enterprise Plus, treat these as an optimization
backlog rather than gaps. Several are also engine-scoped.
- Machine tier and edition: recorded above as context. Tier strings are
edition-scoped, and real sizing risk is covered by the disk, CPU, memory and
connection checks.
- Application behaviour, load testing, networking, IAM, DR rehearsal, capacity
and quota planning: outside the reach of an API-level audit.
A fully green audit indicates that the surveyed configuration aligns with recommended operational best practices.
It does not guarantee uninterrupted availability, nor does it guarantee against localized infrastructure anomalies, underlying hardware faults, or unforeseen workload spikes.
```
End every report with the Scope Note, including runs where nothing failed.
Cloud SQL Peak Event Preparedness: 13 Checks Before Traffic Hits was originally published in Google Cloud – Community on Medium, where people are continuing the conversation by highlighting and responding to this story.
Source Credit: https://medium.com/google-cloud/cloud-sql-peak-event-preparedness-13-checks-before-traffic-hits-b8a52f0f1b52?source=rss—-e52cf94d98af—4
