AppEngine and the Capabilities Service

Google’s App Engine has some scheduled maintenance pretty soon (June 10th (Wednesday) at 4 PM Pacific Time). During this time, both datastore writes and the memcache will be disabled. Now, I have no problem with this – all systems need to have maintenance completed on them, and in general some of this maintenance will involve service disruption.

What I do have a problem with is the suggested way of dealing with the outage. In the scheduled maintenance announcement instructions are given on how a CapabilityDisabledError exception can be intercepted and gracefully dealt with (example code from the announcement below):

from google.appengine.ext import db
from google.appengine.runtime.apiproxy_errors import
CapabilityDisabledError
 
myModel = db.Model()
try:
    myModel.put()
except CapabilityDisabledError:
    # fail gracefully here
    pass

Sure, that will prevent errors being displayed to users of our appengine hosted sites, but wouldn’t we be better off proactively telling users that our sites are in a readonly mode and once again proactively disable data-entry forms and the like. There must be a better way. So off I went for some spelunking through the appengine api. I have to admit it’s pretty interesting in there, with things like ProtocolBuffers to distract you. And then I had it – the “ah hah” moment.

I had come across the little documented (if at all) capabilities api within appengine api (google.appengine.api.capabilities). The __init__.py file in source actually contains some useful code examples which are exactly what we need to proactively deal with the upcoming maintenance:

def RenderHTMLForm(self):
  datastore_readonly = CapabilitySet('datastore_v3', capabilities=['write'])
  if datastore_readonly.may_be_disabled_in(60):
    self.response.out('<p>Not accepting submissions right now: %s</p>' % datastore_readonly.admin_message())
    # ...render form with form elements disabled...
  else:
    # ...render form normally...

Unfortunately, it turns out there is a good reason it isn’t documented, and that is it doesn’t work (admittedly I could be doing something wrong). Displayed below is the callstack when I try to call the is_enabled method on an instance of CapabilitySet (code running at: http://conceptualadvantage.appspot.com/capabilities/).

Traceback (most recent call last):
  File "/base/python_lib/versions/1/google/appengine/ext/webapp/__init__.py", line 501, in __call__
    handler.get(*groups)
  File "/base/data/home/apps/conceptualadvantage/1.334105674119681245/capabilities.py", line 25, in get
    checker.run()
  File "/base/data/home/apps/conceptualadvantage/1.334105674119681245/gaetools/capabilities.py", line 94, in run
    'avail_now': capset.is_enabled(),
  File "/base/python_lib/versions/1/google/appengine/api/capabilities/__init__.py", line 98, in is_enabled
    config = self._get_status()
  File "/base/python_lib/versions/1/google/appengine/api/capabilities/__init__.py", line 167, in _get_status
    self._stub_map.MakeSyncCall('capability_service', 'IsEnabled', req, resp)
  File "/base/python_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 68, in MakeSyncCall
    apiproxy.MakeSyncCall(service, call, request, response)
  File "/base/python_lib/versions/1/google/appengine/api/apiproxy_stub_map.py", line 240, in MakeSyncCall
    stub.MakeSyncCall(service, call, request, response)
  File "/base/python_lib/versions/1/google/appengine/runtime/apiproxy.py", line 183, in MakeSyncCall
    rpc.CheckSuccess()
  File "/base/python_lib/versions/1/google/appengine/api/apiproxy_rpc.py", line 112, in CheckSuccess
    raise self.exception
ArgumentError: An error occurred parsing (locally or remotely) the arguments to capability_service.IsEnabled().

If anyone has experience with the capabilities api in appengine, or can confirm that what I’m seeing is a bug that would be fantastic. I’ve checked for issues in the issue tracker for appengine and can’t see anything related to capabilities. I will happily lodge the issue if it is a genuine bug.

It would be really nice to use these features to ensure we as application developers are offering our application users a great user experience.

Leave a Reply