Attualmente stiamo costruendo un servizio HTTP centrale piccolo e semplice che mappa "identità esterne" (come un ID facebook) a un "interno (uu) id", unico in tutti i nostri servizi per aiutare con l'analisi.Quali tempi di risposta ci si può aspettare da GAE/NDB?
Il primo prototipo in "il nostro stack" (flask + postgresql) è stato eseguito in un giorno. Ma dal momento che vogliamo che il servizio (quasi) non fallisca mai e scala automaticamente, abbiamo deciso di utilizzare Google App Engine.
Dopo una settimana di lettura & provare & analisi comparativa questa domanda emerge:
Che tempi di risposta sono considerati "normali" su App Engine (con NDB)?
Stiamo ottenendo tempi di risposta che sono costantemente 500ms superiori in media e ben al di sopra 1s nel 90percentile.
Ho allegato una versione ridotta del nostro codice qui sotto, sperando che qualcuno possa evidenziare l'evidente difetto. Ci piace molto la scalabilità automatica e lo storage distribuito, ma non possiamo immaginare che i 500ms siano realmente le prestazioni previste nel nostro caso. Il prototipo basato su sql ha risposto molto più velocemente (coerentemente), ospitato su un singolo droke di Heroku usando il postgresql senza cache gratuito (anche con un ORM).
Abbiamo provato entrambe le varianti sincrone e asincrone del codice seguente e ho esaminato il profilo di appstats. Sono sempre chiamate RPC (sia memcache che datastore) che richiedono molto tempo (50ms-100ms), aggravate dal fatto che ci sono sempre più chiamate (ad esempio mc.get() + ds.get() + ds.set () su una scrittura). Abbiamo anche provato a rimandare il più possibile alla coda dei task, senza vantaggi evidenti.
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)
Per completezza la (quasi 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
Yepp, hai ragione sulle prestazioni di dev_appserver (sqlite su ssd ...), quindi testiamo la produzione (anche l'account pagato). Per quanto riguarda le iterazioni, solitamente i test vengono eseguiti per circa 5 minuti. Cerchiamo anche di assicurarci che ogni esecuzione abbia una quantità comparabile di hit/miss (svuotando il datastore/memcache tra le esecuzioni o giocando con l'intervallo 'provider_user_id' è in). – selkie
Una nota: se stai eseguendo un grande benchmark, devi far ruotare il tuo traffico gradualmente (diciamo 5-10 minuti) e poi sostenerlo per un po '(altri 5-10 minuti) per misurare gli effetti realistici. App Engine non farà girare immediatamente le istanze necessarie quando il carico va da 0 a 100; c'è un "governatore" su questo processo per evitare instabilità. –
Ho appena letto sul comportamento di "una scrittura al secondo per gruppo di entità" di HRD. Nel codice sopra, il suo non spiegherebbe i nostri problemi? Ci sono solo una manciata di provider (principalmente facebook) e _Identity_ ha _Provider_ come genitore, rendendoli un _entity group_? – selkie