We are currently creating a small and simple central HTTP service that maps "external identifiers" (for example, facebook identifier) ββto an "internal (uu) id" unique to all of our services to help with analytics.
The first prototype in "our stack" (bulb + postgresql) was completed during the day. But since we want the service to (almost) never interrupt or scale automatically, we decided to use the Google App Engine.
After a week of reading and testing and comparing, this question arises:
What is the βnormalβ response time in App Engine (with NDB)?
We get a response time that constantly exceeds 500 ms on average and well above 1 s in the 90th client.
I have attached the version of our code below, hoping that someone can point out an obvious flaw. We really like autoscaling and distributed storage, but we cannot imagine that 500 ms is the expected performance in our case. The sql-based prototype responded much faster (sequentially), hosted on a single Heroku single, using the free, non-cache postgresql (even with ORM).
We tried both synchronous and asynchronous code options below and looked at the appstats profile. These are always RPC calls (both memcache and datastore), which take a very long time (50 ms-100 ms), which has deteriorated due to the fact that there are always several calls (for example, mc.get () + ds.get () + ds.set () to write). We also tried to postpone the task to the task queue as much as possible without noticeable success.
import json import uuid from google.appengine.ext import ndb import webapp2 from webapp2_extras.routes import RedirectRoute def _parse_request(request): if request.content_type == 'application/json': try: body_json = json.loads(request.body) provider_name = body_json.get('provider_name', None) provider_user_id = body_json.get('provider_user_id', None) except ValueError: return webapp2.abort(400, detail='invalid json') else: provider_name = request.params.get('provider_name', None) provider_user_id = request.params.get('provider_user_id', None) return provider_name, provider_user_id class Provider(ndb.Model): name = ndb.StringProperty(required=True) class Identity(ndb.Model): user = ndb.KeyProperty(kind='GlobalUser') class GlobalUser(ndb.Model): uuid = ndb.StringProperty(required=True) @property def identities(self): return Identity.query(Identity.user==self.key).fetch() class ResolveHandler(webapp2.RequestHandler): @ndb.toplevel def post(self): provider_name, provider_user_id = _parse_request(self.request) if not provider_name or not provider_user_id: return self.abort(400, detail='missing provider_name and/or provider_user_id') identity = ndb.Key(Provider, provider_name, Identity, provider_user_id).get() if identity: user_uuid = identity.user.id() else: user_uuid = uuid.uuid4().hex GlobalUser( id=user_uuid, uuid=user_uuid ).put_async() Identity( parent=ndb.Key(Provider, provider_name), id=provider_user_id, user=ndb.Key(GlobalUser, user_uuid) ).put_async() return webapp2.Response( status='200 OK', content_type='application/json', body = json.dumps({ 'provider_name' : provider_name, 'provider_user_id' : provider_user_id, 'uuid' : user_uuid }) ) app = webapp2.WSGIApplication([ RedirectRoute('/v1/resolve', ResolveHandler, 'resolve', strict_slash=True) ], debug=False)
For completeness of use (almost by default) app.yaml
application: GAE_APP_IDENTIFIER version: 1 runtime: python27 api_version: 1 threadsafe: yes handlers: - url: .* script: main.app libraries: - name: webapp2 version: 2.5.2 - name: webob version: 1.2.3 inbound_services: - warmup