Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/framework/teardown.py b/framework/teardown.py
new file mode 100644
index 0000000..05ebb29
--- /dev/null
+++ b/framework/teardown.py
@@ -0,0 +1,41 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""A handler run on Flask request teardown."""
+
+import logging
+import os
+
+from google.appengine.api import runtime
+from googleapiclient import discovery
+from googleapiclient import errors
+from oauth2client import client
+
+import settings
+
+_MAXIMUM_MEMORY_USAGE = 2000
+
+
+def Teardown(_exc):
+  if settings.local_mode:
+    return
+
+  # Stop the instance if it's using too much memory.
+  memory_usage = runtime.memory_usage().average10m
+  if memory_usage < _MAXIMUM_MEMORY_USAGE:
+    return
+
+  credentials = client.GoogleCredentials.get_application_default()
+  appengine = discovery.build('appengine', 'v1', credentials=credentials)
+  delete = appengine.apps().services().versions().instances().delete(
+      appsId=os.environ.get('GAE_APPLICATION').split('~')[-1],
+      servicesId=os.environ.get('GAE_SERVICE'),
+      versionsId=os.environ.get('GAE_VERSION'),
+      instancesId=os.environ.get('GAE_INSTANCE'))
+  try:
+    delete.execute()
+  except errors.HttpError as e:
+    if e.status_code != 404:
+      raise
+  else:
+    logging.critical('Deleted instance using %d MB of memory.' % memory_usage)