Flask App on Google App Engine Fails to Fetch Data from Cloud SQL with "500 Server Error"

100 views Asked by At

I've developed a Flask app that communicates with a MySQL 5.7 database on Google Cloud SQL, with both the app and the database residing within the same Google Cloud Project. Locally, my app communicates with the database flawlessly using Cloud SQL Proxy. My CORS configuration in the main.py currently allows all domains, so CORS should not be causing any issues. Despite this, after deploying to Google App Engine, any attempt to fetch data from the database times out, leading to a '500 Server Error'.

Local Setup (working): Flask app connected through Cloud SQL Proxy using Unix sockets.

Successful curl test:

curl 'http://localhost:5000/api/projects/testemail1%40gmail.com'

Production Setup (failing): Flask app deployed on Google App Engine.

Failing curl test:

curl 'https://app-engine-url/api/projects/testemail1%40gmail.com'

Error Received:

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>500 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered an error and could not complete your request.<p>Please try again in 30 seconds.</h2>
</body></html>

Error Log in App Engine Logs

[Date:Time - Timezone] GET /api/projects/[email protected] HTTP/1.1 503 - https://app-engine-url/ "Mozilla/5.0 (Operating System; Architecture) AppleWebKit/Version (KHTML, like Gecko) Safari/Version" "yourdeployment.yourhost.com" ms=Time cpu_ms=0 cpm_usd=0 loading_request=0 instance=- app_engine_release=ReleaseVersion trace_id=TraceID
{
httpRequest: {
status: 503
}
insertId: "InsertID"
labels: {
clone_id: ""
}
logName: "projects/yourproject/logs/appengine.googleapis.com%2Frequest_log"
operation: {
first: true
id: "OperationID"
last: true
producer: "appengine.googleapis.com/request_id"
}
protoPayload: {
@type: "TypeURL"
appEngineRelease: "ReleaseVersion"
appId: "AppID"
endTime: "EndTime"
finished: true
first: true
host: "yourdeployment.yourhost.com"
httpVersion: "HTTP/1.1"
instanceIndex: InstanceIndex
ip: "IP_Address"
latency: "52.228289s"
line: [
0: {
logMessage: "Request was aborted after waiting too long to attempt to service your request."
severity: "ERROR"
time: "Time"
}
]
method: "GET"
pendingTime: "PendingTime"
referrer: "https://app-engine-url/"
requestId: "RequestID"
resource: "/api/projects/[email protected]"
spanId: "SpanID"
startTime: "StartTime"
status: 503
traceId: "TraceID"
urlMapEntry: "UrlMapEntry"
userAgent: "UserAgentString"
versionId: "VersionID"
}
receiveTimestamp: "ReceiveTimestamp"
resource: {
labels: {
module_id: "ModuleID"
project_id: "ProjectID"
version_id: "VersionID"
zone: "Zone"
}
type: "ResourceType"
}
severity: "ERROR"
spanId: "SpanID"
timestamp: "Timestamp"
trace: "TracePath"
}

Mapper Configuration

# Mapper Class for MySQL Connection via Cloud SQL Proxy (Local) and Unix Socket (Production)
import os
import mysql.connector as connector
from contextlib import AbstractContextManager
from abc import ABC

class Mapper(AbstractContextManager, ABC):
    def __init__(self):
        self._cnx = None

    def __enter__(self):
        # Check if running in GAE environment
        gae_env = os.getenv('GAE_ENV')
        if gae_env and gae_env.startswith('standard'):
            # Production environment: connect using Unix socket
            self._cnx = connector.connect(
                user='root', password='[password]', 
                unix_socket='/cloudsql/[instance-connection-name]',
                database='[database-name]'
            )
        else:
            # Local environment: connect via Cloud SQL Proxy
            socket_path = '/local/path/to/cloudsql/[instance-connection-name]'
            self._cnx = connector.connect(
                user='root', password='[password]', 
                unix_socket=socket_path, database='[database-name]'
            )
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self._cnx:
            self._cnx.close()

Google App Engine app.yaml Configuration

runtime: python38
env: standard
instance_class: B1

manual_scaling:
  instances: 4

beta_settings:
  cloud_sql_instances: [instance-connection-name]


handlers:
# Serve the API requests
- url: /api/.*
  script: auto

# Serve the index.html file for any other requests
- url: /.*
  static_files: build/index.html
  upload: build/index.html

env_variables:
  GAE_ENV: 'standard'

main.py Configuration

from flask import Flask, send_from_directory
from flask_restx import Api, Resource, fields
from flask_cors import CORS

# [Omitted: Custom imports for security, business logic classes, etc.]

app = Flask(__name__, static_folder='build', static_url_path='')
app.config['CORS_HEADERS'] = 'Content-Type'
CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)

@app.errorhandler(404)
def not_found(e):
    return app.send_static_file('index.html')

@app.route('/')
def index():
    return app.send_static_file('index.html')

api = Api(app, version='1.0', title='API',
          description='API for a project management tool')

api_namespace = api.namespace('api', 
                                path='/api',
                                description='App description')
# [Omitted: Additional routes and API endpoints]

if (__name__ == "__main__"):
    app.run(debug=True) # testing on localhost
  • Activated both Cloud SQL Admin API and Cloud SQL API for database interaction.
  • Executed gcloud components install app-engine-python for App Engine compatibility.
  • Confirmed that APIs are enabled and both environments use the same MySQL version.
  • Locally, fetch requests to the database are quick, but on Google App Engine, they time out.
  • Successfully connected to the database locally using the Cloud SQL Proxy TCP Connection as well.
  • Verified that the Cloud SQL instance has a public IP.
  • Ensured the service account possesses the Cloud SQL Client role or higher.

Given the above, what could be causing the fetch to fail in production, and how can I further diagnose and resolve the issue?

Note: Sensitive information has been replaced with placeholders for security.

0

There are 0 answers