SlideShare a Scribd company logo
1 of 40
Download to read offline
PyCon6 Apr2015, Florence, Italy
Art & Music VS AppEngine
Thomas Alisi, Solution Architect
@grudelsud @stinkdigital
photo courtesy of https://www.flickr.com/photos/8064990@N08/
See our 2015 showreel
Stinkdigital is an interactive production company,
working with clients and advertising agencies
worldwide.
Our services include creative concepting, design
and high-end execution. We create everything from
live-action films and websites, through to mobile
apps and installations.
Does this number look familiar to you?
86,400
Does this number look familiar to you?
86,400
= 60’’ x 60’ x 24h[number of seconds in 1 day]
What about this one?
31,536,000
What about this one?
31,536,000
= 86,400’’ x 365d[number of seconds in 1 year]
OK, now try and guess the last one
252,000,000
OK, now try and guess the last one
252,000,000
= 31.5M’’ x 8y[number of seconds in 8 years]
but also…
252,000,000
= 36M x 7’’[7 seconds saved for each of the 36M visits
we had during the first 6 months on DevArt]
How did we do it?
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
Revolutions in Sound - RedBull, Google+
http://archive.revolutionsinsound.com/
DevArt - Google CL
https://devart.withgoogle.com/
Inside Abbey Road - Google CL
https://insideabbeyroad.withgoogle.com/
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
- Grunt, Gulp, Browserify, Webpack… AAARGH!
- we like vanilla JS and have been fan of Gulp 

(at least over the past 10 minutes…)
- we (I) tend to use a super simple Gulp-Browserify-
AngularJS boilerplate 

https://github.com/grudelsud/angularjs-gulp-browserify-
boilerplate
- but there are other good examples too

e.g. https://github.com/unit9/coffee-bone
divide et impera
- GAE is not opinionated (which is good)
- default webapp2 shipped with GAE is really super simple
(a bit too simple…)
- Flask is OK and does not have preferences for a specific
ORM (which is great)
- don’t use Django on GAE unless you really want to 

(e.g. use legacy modules or deploy something really
quick)
class Jsonifiable(ndb.Model):
def from_dict(cls, dict):
pass
def to_dict(self, async=False):
pass
def resolve_future_blobs(cls, async_blobs):
pass
class JsonRestHandler(webapp2.RequestHandler):
JSON_MIMETYPE = "application/json"
def write(self, data):
self.response.out.write(data)
def send_success(self, obj=None, cache_expiry=None):
self.response.headers["Content-Type"] = self.JSON_MIMETYPE
self.write(json.dumps(obj, cls=JsonifiableEncoder))
webapp2 bespoke REST micro-framework
asynchronous conversion
bespoke encoder
basic permission check (skipped here)
extend ndb.Model
extend webapp2.RequestHandler
kibble_authenticator = ModelAuthenticatior()
admin = kibble.Kibble(
'admin',
__name__,
kibble_authenticator,
label='Admin',
static_url_path='/kibble/static',
default_gcs_bucket=app_identity.get_default_gcs_bucket_name(),
)
security_headers.apply(app)
class CustomJsonEncoder(flask.json.JSONEncoder):
def default(self, obj):
try:
if isinstance(obj, blobstore.BlobKey):
return str(obj)
if isinstance(obj, fields.Vertex):
return [obj.x, obj.y, obj.z]
if isinstance(obj, GCSFile):
return obj.cloudstore_url
if isinstance(obj, ndb.Key):
return obj.flat()
except TypeError:
pass
return super(CustomJsonEncoder, self).default(obj)
app = flask.Flask(__name__, template_folder='../templates')
app.json_encoder = CustomJsonEncoder
admin.autodiscover(paths=['admin'])
app.register_blueprint(admin, url_prefix='/admin')
Flask Blueprints and GAE
https://github.com/xlevus/flask-kibble
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
this always seems a good way to start
class ClubNight(BaseModel):
name = ndb.StringProperty()
content = ContentProperty(ndb.TextProperty)
blob_key_logo = ContentProperty(ndb.BlobKeyProperty)
genre = ndb.KeyProperty(kind=’Genre’)
website = ContentProperty(ndb.StringProperty)
address = ContentProperty(ndb.TextProperty)
location = ContentProperty(ndb.GeoPtProperty)
class Connection(ndb.Model):
from_key = ndb.KeyProperty()
to_key = ndb.KeyProperty()
abstraction
class ClubNight(BaseModel):
name = ndb.StringProperty()
content = ContentProperty(ndb.TextProperty)
blob_key_logo = ContentProperty(ndb.BlobKeyProperty)
genre = ndb.KeyProperty(kind=’Genre’)
website = ContentProperty(ndb.StringProperty)
address = ContentProperty(ndb.TextProperty)
location = ContentProperty(ndb.GeoPtProperty)
class Connection(ndb.Model):
from_key = ndb.KeyProperty()
to_key = ndb.KeyProperty()
to_name = ndb.StringProperty()
to_kind = ndb.StringProperty()
to_slug = ndb.StringProperty()
to_genre_colour = ndb.StringProperty()
to_image = ndb.BlobKeyProperty()
to_popularity = ndb.IntegerProperty()
is_published = ndb.BooleanProperty()
is_public = ndb.BooleanProperty(default=False)
is_featured = ndb.BooleanProperty(default=False)
reality
class Vertex(namedtuple('Vertex', ['x', 'y', 'z'])):
def __json__(self):
return json.dumps(self)
def __str__(self):
return 'x={0.x:.2f}, y={0.y:.2f}, z={0.z:.2f}'.format(self)
class VertexProperty(ndb.StringProperty):
# Dummy XYZ field, used to quickly add validation through
# admin.base.ModelConverter
def _validate(self, value):
if not isinstance(value, (tuple, Vertex)):
raise TypeError("Expected Vertex got %r" % repr(value))
if len(value) != 3:
raise TypeError("Expected 3-tuple/Vertex, got %r" % repr(value))
def _to_base_type(self, value):
return "%s,%s,%s" % value
def _from_base_type(self, value):
if value:
return Vertex(*[float(x) for x in value.split(",")])
return None
finding the sweet spot
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
The get_serving_url() method allows you to generate a stable, dedicated URL for
serving web-suitable image thumbnails. You simply store a single copy of your
original image in Blobstore, and then request a high-performance per-image URL.
[https://cloud.google.com/appengine/docs/python/images/]
ex.
// Resize the image to 32 pixels (aspect-ratio preserved)
http://your_app_id.appspot.com/randomStringImageId=s32
// Crop the image to 32 pixels
http://your_app_id.appspot.com/randomStringImageId=s32-c
Due diligence
=s500 =s100
- success == sleepless_nights (not for me! :P)
- PR scheduled on all major funnels:
- Google homepage
- Youtube homepage
- all major blogs (the verge, wired, etc.)
- London silicon roundabout
- 1.2M unique visits, 10M views during the FIRST 48H
si vis pacem, para bellum
from admin.publish_pipeline import Publish
rec = urlmap.PublishRecord.new()
p = Publish('admin.web.app', version=rec.key.id())
p.start()
[…]
class Publish(Pipeline):
"""
Root Pipeline. Creates a :py:class:`common.models.urlmap.PublishRecord` for
the pipeline, and spawns children.
On completion, sets publish record to completed.
"""
def run(self, app, locales=None, version=None):
rec = urlmap.PublishRecord.get_by_id(version)
rec.pipeline_id = self.root_pipeline_id
rec.put()
urlmaps = yield URLMaps(version)
with After(urlmaps):
static = yield WriteStaticFiles.pipeline(app, version)
sitemap = yield PublishSitemap(app, version)
short_urls = yield CreateShortUrls.pipeline(app, version)
with After(static, sitemap, short_urls):
json = yield PublishJSON(app, version)
with After(json):
yield CleanShortUrls(app, version)
yield Cleanup.pipeline()
static publishing pipeline
https://github.com/GoogleCloudPlatform/appengine-mapreduce/
create the pipeline and run process
extend mapreduce pipeline
spawn separate generators
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
GAE docs are rubbish! :) i.e. read it, then forget it:
https://cloud.google.com/appengine/articles/load_test
3rd party services are OK, but run your own if you can
create a meaningful simulation of users’ behaviour
hit it as hard as you can, but don’t forget your wallet!
class ApiNav(TaskSet):
@task(1)
def api_global(self):
self.client.get('/api/global?locale=%s' % langs[random.randint(0, len(langs)-1)], **kwargs)
@task(1)
def api_user(self):
self.client.get('/api/user', **kwargs)
@task(4)
def api_gallery(self):
self.client.get('/api/gallery?i=0&l=15', **kwargs)
@task(8)
def api_search(self):
self.client.get('/api/gallery?i=0&l=15&q=%s' % terms[random.randint(0, len(terms)-1)], **kwargs)
@task(6)
def api_feeling_lucky(self):
self.client.get('/api/page/feeling_lucky', **kwargs)
@task(2)
def api_big_gallery(self):
self.client.get('/api/gallery?i=0&l=30', **kwargs)
@task(2)
def api_featured(self):
self.client.get('/api/gallery?i=0&l=15&t=featured', **kwargs)
class MyLocust(HttpLocust):
host = 'https://sd-goog-devart.appspot.com'
task_set = ApiNav
min_wait = 5000
max_wait = 15000
locust
init data
gallery
random search
random project page
categorized views
- approximately 5000 concurrent user hitting the backend
API with a "casual navigation" simulation from different
location (London, New York, AWS data centre in Ireland)
- 85 running instances (class F2) at peak
- no errors reported other than random https sockets
timeout
- average response times
- < 2s for gallery content navigation
- < 1s for singe project page navitation
- < 3s for static contend (loaded just once)
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
1. Google App Engine (GAE) and AngularJS
2. data structures
3. assets management
4. load testing
5. GAE benchmarking tool and task inspector
6. GAE asynchronous API
@classmethod
def fix_dict(cls, dict, async=False):
async_blobs = []
def _fix_dict(k, v):
# blob
if isinstance(v, blobstore.BlobKey):
try:
# create futures and put them apart, we'll resolve these later
output = {'key': str(v), 'url': '', 'rpc': images.get_serving_url_async(v, secure_url=True)}
async_blobs.append(output)
return output
except BaseException as e:
# logging.warn('error while fetching serving url for [%s] maybe using corrupted image?' % (v,))
return None
for k, v in dict.iteritems():
dict[k] = _fix_dict(k, v)
if async is True:
return dict, async_blobs
else:
cls.resolve_future_blobs(async_blobs)
return dict
@classmethod
def resolve_future_blobs(cls, async_blobs):
# resolve futures
for blob in async_blobs:
try:
blob['url'] = blob['rpc'].get_result()
except BaseException:
blob['url'] = ''
del(blob['rpc'])
get_serving_url_async
1. get async url
3. get real url
2. resolve future blobs
Thanks!
uh, just few notes:
1. we are hiring! check www.stinkdigital.com/careers and send us your CV -
careers@stinkdigital.com
2. we organise a Meetup in London with UNIT9 and B-Reel, get in touch!
tomalisi@stinkdigital.com
3. www.slideshare.net/grudelsud

More Related Content

What's hot

Android Sliding Menu dengan Navigation Drawer
Android Sliding Menu dengan Navigation DrawerAndroid Sliding Menu dengan Navigation Drawer
Android Sliding Menu dengan Navigation DrawerAgus Haryanto
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projectsIgnacio Martín
 
Get started with YUI
Get started with YUIGet started with YUI
Get started with YUIAdam Lu
 
Ultimate Introduction To AngularJS
Ultimate Introduction To AngularJSUltimate Introduction To AngularJS
Ultimate Introduction To AngularJSJacopo Nardiello
 
Everything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View ControllersEverything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View ControllersBrian Gesiak
 
Android Wear Essentials
Android Wear EssentialsAndroid Wear Essentials
Android Wear EssentialsNilhcem
 
Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...
Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...
Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...Alessandro Nadalin
 
Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010Dave Furfero
 
Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010guest3a77e5d
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaExoLeaders.com
 
Primer vistazo al computer vision | 4Sessions Feb17
Primer vistazo al computer vision | 4Sessions Feb17Primer vistazo al computer vision | 4Sessions Feb17
Primer vistazo al computer vision | 4Sessions Feb17[T]echdencias
 
Android basic 4 Navigation Drawer
Android basic 4 Navigation DrawerAndroid basic 4 Navigation Drawer
Android basic 4 Navigation DrawerEakapong Kattiya
 
Introducing Vuex in your project
Introducing Vuex in your projectIntroducing Vuex in your project
Introducing Vuex in your projectDenny Biasiolli
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applicationsPaweł Żurowski
 
2 years of angular: lessons learned
2 years of angular: lessons learned2 years of angular: lessons learned
2 years of angular: lessons learnedDirk Luijk
 
Architecture for scalable Angular applications (with introduction and extende...
Architecture for scalable Angular applications (with introduction and extende...Architecture for scalable Angular applications (with introduction and extende...
Architecture for scalable Angular applications (with introduction and extende...Paweł Żurowski
 

What's hot (18)

Android Sliding Menu dengan Navigation Drawer
Android Sliding Menu dengan Navigation DrawerAndroid Sliding Menu dengan Navigation Drawer
Android Sliding Menu dengan Navigation Drawer
 
droidparts
droidpartsdroidparts
droidparts
 
Integrating React.js with PHP projects
Integrating React.js with PHP projectsIntegrating React.js with PHP projects
Integrating React.js with PHP projects
 
Get started with YUI
Get started with YUIGet started with YUI
Get started with YUI
 
Scriptaculous
ScriptaculousScriptaculous
Scriptaculous
 
Ultimate Introduction To AngularJS
Ultimate Introduction To AngularJSUltimate Introduction To AngularJS
Ultimate Introduction To AngularJS
 
Everything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View ControllersEverything You (N)ever Wanted to Know about Testing View Controllers
Everything You (N)ever Wanted to Know about Testing View Controllers
 
Android Wear Essentials
Android Wear EssentialsAndroid Wear Essentials
Android Wear Essentials
 
Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...
Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...
Hey, I just met AngularJS, and this is crazy, so here’s my JavaScript, let’s ...
 
Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010
 
Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010Making Ajax Sexy, JSConf 2010
Making Ajax Sexy, JSConf 2010
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
Primer vistazo al computer vision | 4Sessions Feb17
Primer vistazo al computer vision | 4Sessions Feb17Primer vistazo al computer vision | 4Sessions Feb17
Primer vistazo al computer vision | 4Sessions Feb17
 
Android basic 4 Navigation Drawer
Android basic 4 Navigation DrawerAndroid basic 4 Navigation Drawer
Android basic 4 Navigation Drawer
 
Introducing Vuex in your project
Introducing Vuex in your projectIntroducing Vuex in your project
Introducing Vuex in your project
 
Architecture for scalable Angular applications
Architecture for scalable Angular applicationsArchitecture for scalable Angular applications
Architecture for scalable Angular applications
 
2 years of angular: lessons learned
2 years of angular: lessons learned2 years of angular: lessons learned
2 years of angular: lessons learned
 
Architecture for scalable Angular applications (with introduction and extende...
Architecture for scalable Angular applications (with introduction and extende...Architecture for scalable Angular applications (with introduction and extende...
Architecture for scalable Angular applications (with introduction and extende...
 

Similar to Art & music vs Google App Engine

Performance and Optmization - a technical talk at Frontend London
Performance and Optmization - a technical talk at Frontend LondonPerformance and Optmization - a technical talk at Frontend London
Performance and Optmization - a technical talk at Frontend Londonthomas alisi
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...Fabio Franzini
 
The Theory Of The Dom
The Theory Of The DomThe Theory Of The Dom
The Theory Of The Domkaven yan
 
MLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to ProductionMLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to ProductionBigML, Inc
 
The Web on OSGi: Here's How
The Web on OSGi: Here's HowThe Web on OSGi: Here's How
The Web on OSGi: Here's Howmrdon
 
WebGL: GPU acceleration for the open web
WebGL: GPU acceleration for the open webWebGL: GPU acceleration for the open web
WebGL: GPU acceleration for the open webpjcozzi
 
Professional JavaScript: AntiPatterns
Professional JavaScript: AntiPatternsProfessional JavaScript: AntiPatterns
Professional JavaScript: AntiPatternsMike Wilcox
 
Viking academy backbone.js
Viking academy  backbone.jsViking academy  backbone.js
Viking academy backbone.jsBert Wijnants
 
JBUG 11 - Django-The Web Framework For Perfectionists With Deadlines
JBUG 11 - Django-The Web Framework For Perfectionists With DeadlinesJBUG 11 - Django-The Web Framework For Perfectionists With Deadlines
JBUG 11 - Django-The Web Framework For Perfectionists With DeadlinesTikal Knowledge
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to javaciklum_ods
 
AngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile ServicesAngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile ServicesRainer Stropek
 
Data visualization in python/Django
Data visualization in python/DjangoData visualization in python/Django
Data visualization in python/Djangokenluck2001
 
Viktor Tsykunov: Azure Machine Learning Service
Viktor Tsykunov: Azure Machine Learning ServiceViktor Tsykunov: Azure Machine Learning Service
Viktor Tsykunov: Azure Machine Learning ServiceLviv Startup Club
 
Designing REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDesigning REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDmitriy Sobko
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
SharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure FunctionsSharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure FunctionsSébastien Levert
 

Similar to Art & music vs Google App Engine (20)

Performance and Optmization - a technical talk at Frontend London
Performance and Optmization - a technical talk at Frontend LondonPerformance and Optmization - a technical talk at Frontend London
Performance and Optmization - a technical talk at Frontend London
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...WebNet Conference 2012 - Designing complex applications using html5 and knock...
WebNet Conference 2012 - Designing complex applications using html5 and knock...
 
AngularJS and SPA
AngularJS and SPAAngularJS and SPA
AngularJS and SPA
 
The Theory Of The Dom
The Theory Of The DomThe Theory Of The Dom
The Theory Of The Dom
 
MLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to ProductionMLSEV Virtual. From my First BigML Project to Production
MLSEV Virtual. From my First BigML Project to Production
 
The Web on OSGi: Here's How
The Web on OSGi: Here's HowThe Web on OSGi: Here's How
The Web on OSGi: Here's How
 
WebGL: GPU acceleration for the open web
WebGL: GPU acceleration for the open webWebGL: GPU acceleration for the open web
WebGL: GPU acceleration for the open web
 
Professional JavaScript: AntiPatterns
Professional JavaScript: AntiPatternsProfessional JavaScript: AntiPatterns
Professional JavaScript: AntiPatterns
 
Viking academy backbone.js
Viking academy  backbone.jsViking academy  backbone.js
Viking academy backbone.js
 
JBUG 11 - Django-The Web Framework For Perfectionists With Deadlines
JBUG 11 - Django-The Web Framework For Perfectionists With DeadlinesJBUG 11 - Django-The Web Framework For Perfectionists With Deadlines
JBUG 11 - Django-The Web Framework For Perfectionists With Deadlines
 
Django tricks (2)
Django tricks (2)Django tricks (2)
Django tricks (2)
 
End-to-end testing with geb
End-to-end testing with gebEnd-to-end testing with geb
End-to-end testing with geb
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
AngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile ServicesAngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile Services
 
Data visualization in python/Django
Data visualization in python/DjangoData visualization in python/Django
Data visualization in python/Django
 
Viktor Tsykunov: Azure Machine Learning Service
Viktor Tsykunov: Azure Machine Learning ServiceViktor Tsykunov: Azure Machine Learning Service
Viktor Tsykunov: Azure Machine Learning Service
 
SCALA - Functional domain
SCALA -  Functional domainSCALA -  Functional domain
SCALA - Functional domain
 
Designing REST API automation tests in Kotlin
Designing REST API automation tests in KotlinDesigning REST API automation tests in Kotlin
Designing REST API automation tests in Kotlin
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
SharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure FunctionsSharePoint Framework, Angular and Azure Functions
SharePoint Framework, Angular and Azure Functions
 

Recently uploaded

办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书zdzoqco
 
SCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is prediSCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is predieusebiomeyer
 
Unidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptxUnidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptxmibuzondetrabajo
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxDyna Gilbert
 
ETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptxETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptxNIMMANAGANTI RAMAKRISHNA
 
IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119APNIC
 
Company Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptxCompany Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptxMario
 
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书rnrncn29
 
TRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptxTRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptxAndrieCagasanAkio
 
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书rnrncn29
 
Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa494f574xmv
 

Recently uploaded (11)

办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
办理多伦多大学毕业证成绩单|购买加拿大UTSG文凭证书
 
SCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is prediSCM Symposium PPT Format Customer loyalty is predi
SCM Symposium PPT Format Customer loyalty is predi
 
Unidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptxUnidad 4 – Redes de ordenadores (en inglés).pptx
Unidad 4 – Redes de ordenadores (en inglés).pptx
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptx
 
ETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptxETHICAL HACKING dddddddddddddddfnandni.pptx
ETHICAL HACKING dddddddddddddddfnandni.pptx
 
IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119IP addressing and IPv6, presented by Paul Wilson at IETF 119
IP addressing and IPv6, presented by Paul Wilson at IETF 119
 
Company Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptxCompany Snapshot Theme for Business by Slidesgo.pptx
Company Snapshot Theme for Business by Slidesgo.pptx
 
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
『澳洲文凭』买詹姆士库克大学毕业证书成绩单办理澳洲JCU文凭学位证书
 
TRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptxTRENDS Enabling and inhibiting dimensions.pptx
TRENDS Enabling and inhibiting dimensions.pptx
 
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
『澳洲文凭』买拉筹伯大学毕业证书成绩单办理澳洲LTU文凭学位证书
 
Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa
 

Art & music vs Google App Engine

  • 1. PyCon6 Apr2015, Florence, Italy Art & Music VS AppEngine Thomas Alisi, Solution Architect @grudelsud @stinkdigital photo courtesy of https://www.flickr.com/photos/8064990@N08/
  • 2. See our 2015 showreel Stinkdigital is an interactive production company, working with clients and advertising agencies worldwide. Our services include creative concepting, design and high-end execution. We create everything from live-action films and websites, through to mobile apps and installations.
  • 3. Does this number look familiar to you? 86,400
  • 4. Does this number look familiar to you? 86,400 = 60’’ x 60’ x 24h[number of seconds in 1 day]
  • 5. What about this one? 31,536,000
  • 6. What about this one? 31,536,000 = 86,400’’ x 365d[number of seconds in 1 year]
  • 7. OK, now try and guess the last one 252,000,000
  • 8. OK, now try and guess the last one 252,000,000 = 31.5M’’ x 8y[number of seconds in 8 years]
  • 9. but also… 252,000,000 = 36M x 7’’[7 seconds saved for each of the 36M visits we had during the first 6 months on DevArt]
  • 10. How did we do it? 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 11. Revolutions in Sound - RedBull, Google+ http://archive.revolutionsinsound.com/ DevArt - Google CL https://devart.withgoogle.com/ Inside Abbey Road - Google CL https://insideabbeyroad.withgoogle.com/
  • 12.
  • 13. 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 14. - Grunt, Gulp, Browserify, Webpack… AAARGH! - we like vanilla JS and have been fan of Gulp 
 (at least over the past 10 minutes…) - we (I) tend to use a super simple Gulp-Browserify- AngularJS boilerplate 
 https://github.com/grudelsud/angularjs-gulp-browserify- boilerplate - but there are other good examples too
 e.g. https://github.com/unit9/coffee-bone divide et impera
  • 15. - GAE is not opinionated (which is good) - default webapp2 shipped with GAE is really super simple (a bit too simple…) - Flask is OK and does not have preferences for a specific ORM (which is great) - don’t use Django on GAE unless you really want to 
 (e.g. use legacy modules or deploy something really quick)
  • 16. class Jsonifiable(ndb.Model): def from_dict(cls, dict): pass def to_dict(self, async=False): pass def resolve_future_blobs(cls, async_blobs): pass class JsonRestHandler(webapp2.RequestHandler): JSON_MIMETYPE = "application/json" def write(self, data): self.response.out.write(data) def send_success(self, obj=None, cache_expiry=None): self.response.headers["Content-Type"] = self.JSON_MIMETYPE self.write(json.dumps(obj, cls=JsonifiableEncoder)) webapp2 bespoke REST micro-framework asynchronous conversion bespoke encoder basic permission check (skipped here) extend ndb.Model extend webapp2.RequestHandler
  • 17. kibble_authenticator = ModelAuthenticatior() admin = kibble.Kibble( 'admin', __name__, kibble_authenticator, label='Admin', static_url_path='/kibble/static', default_gcs_bucket=app_identity.get_default_gcs_bucket_name(), ) security_headers.apply(app) class CustomJsonEncoder(flask.json.JSONEncoder): def default(self, obj): try: if isinstance(obj, blobstore.BlobKey): return str(obj) if isinstance(obj, fields.Vertex): return [obj.x, obj.y, obj.z] if isinstance(obj, GCSFile): return obj.cloudstore_url if isinstance(obj, ndb.Key): return obj.flat() except TypeError: pass return super(CustomJsonEncoder, self).default(obj) app = flask.Flask(__name__, template_folder='../templates') app.json_encoder = CustomJsonEncoder admin.autodiscover(paths=['admin']) app.register_blueprint(admin, url_prefix='/admin') Flask Blueprints and GAE https://github.com/xlevus/flask-kibble
  • 18. 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 19.
  • 20. this always seems a good way to start
  • 21. class ClubNight(BaseModel): name = ndb.StringProperty() content = ContentProperty(ndb.TextProperty) blob_key_logo = ContentProperty(ndb.BlobKeyProperty) genre = ndb.KeyProperty(kind=’Genre’) website = ContentProperty(ndb.StringProperty) address = ContentProperty(ndb.TextProperty) location = ContentProperty(ndb.GeoPtProperty) class Connection(ndb.Model): from_key = ndb.KeyProperty() to_key = ndb.KeyProperty() abstraction
  • 22. class ClubNight(BaseModel): name = ndb.StringProperty() content = ContentProperty(ndb.TextProperty) blob_key_logo = ContentProperty(ndb.BlobKeyProperty) genre = ndb.KeyProperty(kind=’Genre’) website = ContentProperty(ndb.StringProperty) address = ContentProperty(ndb.TextProperty) location = ContentProperty(ndb.GeoPtProperty) class Connection(ndb.Model): from_key = ndb.KeyProperty() to_key = ndb.KeyProperty() to_name = ndb.StringProperty() to_kind = ndb.StringProperty() to_slug = ndb.StringProperty() to_genre_colour = ndb.StringProperty() to_image = ndb.BlobKeyProperty() to_popularity = ndb.IntegerProperty() is_published = ndb.BooleanProperty() is_public = ndb.BooleanProperty(default=False) is_featured = ndb.BooleanProperty(default=False) reality
  • 23. class Vertex(namedtuple('Vertex', ['x', 'y', 'z'])): def __json__(self): return json.dumps(self) def __str__(self): return 'x={0.x:.2f}, y={0.y:.2f}, z={0.z:.2f}'.format(self) class VertexProperty(ndb.StringProperty): # Dummy XYZ field, used to quickly add validation through # admin.base.ModelConverter def _validate(self, value): if not isinstance(value, (tuple, Vertex)): raise TypeError("Expected Vertex got %r" % repr(value)) if len(value) != 3: raise TypeError("Expected 3-tuple/Vertex, got %r" % repr(value)) def _to_base_type(self, value): return "%s,%s,%s" % value def _from_base_type(self, value): if value: return Vertex(*[float(x) for x in value.split(",")]) return None finding the sweet spot
  • 24.
  • 25. 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 26. The get_serving_url() method allows you to generate a stable, dedicated URL for serving web-suitable image thumbnails. You simply store a single copy of your original image in Blobstore, and then request a high-performance per-image URL. [https://cloud.google.com/appengine/docs/python/images/] ex. // Resize the image to 32 pixels (aspect-ratio preserved) http://your_app_id.appspot.com/randomStringImageId=s32 // Crop the image to 32 pixels http://your_app_id.appspot.com/randomStringImageId=s32-c Due diligence =s500 =s100
  • 27.
  • 28. - success == sleepless_nights (not for me! :P) - PR scheduled on all major funnels: - Google homepage - Youtube homepage - all major blogs (the verge, wired, etc.) - London silicon roundabout - 1.2M unique visits, 10M views during the FIRST 48H si vis pacem, para bellum
  • 29. from admin.publish_pipeline import Publish rec = urlmap.PublishRecord.new() p = Publish('admin.web.app', version=rec.key.id()) p.start() […] class Publish(Pipeline): """ Root Pipeline. Creates a :py:class:`common.models.urlmap.PublishRecord` for the pipeline, and spawns children. On completion, sets publish record to completed. """ def run(self, app, locales=None, version=None): rec = urlmap.PublishRecord.get_by_id(version) rec.pipeline_id = self.root_pipeline_id rec.put() urlmaps = yield URLMaps(version) with After(urlmaps): static = yield WriteStaticFiles.pipeline(app, version) sitemap = yield PublishSitemap(app, version) short_urls = yield CreateShortUrls.pipeline(app, version) with After(static, sitemap, short_urls): json = yield PublishJSON(app, version) with After(json): yield CleanShortUrls(app, version) yield Cleanup.pipeline() static publishing pipeline https://github.com/GoogleCloudPlatform/appengine-mapreduce/ create the pipeline and run process extend mapreduce pipeline spawn separate generators
  • 30. 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 31. GAE docs are rubbish! :) i.e. read it, then forget it: https://cloud.google.com/appengine/articles/load_test 3rd party services are OK, but run your own if you can create a meaningful simulation of users’ behaviour hit it as hard as you can, but don’t forget your wallet!
  • 32. class ApiNav(TaskSet): @task(1) def api_global(self): self.client.get('/api/global?locale=%s' % langs[random.randint(0, len(langs)-1)], **kwargs) @task(1) def api_user(self): self.client.get('/api/user', **kwargs) @task(4) def api_gallery(self): self.client.get('/api/gallery?i=0&l=15', **kwargs) @task(8) def api_search(self): self.client.get('/api/gallery?i=0&l=15&q=%s' % terms[random.randint(0, len(terms)-1)], **kwargs) @task(6) def api_feeling_lucky(self): self.client.get('/api/page/feeling_lucky', **kwargs) @task(2) def api_big_gallery(self): self.client.get('/api/gallery?i=0&l=30', **kwargs) @task(2) def api_featured(self): self.client.get('/api/gallery?i=0&l=15&t=featured', **kwargs) class MyLocust(HttpLocust): host = 'https://sd-goog-devart.appspot.com' task_set = ApiNav min_wait = 5000 max_wait = 15000 locust init data gallery random search random project page categorized views
  • 33. - approximately 5000 concurrent user hitting the backend API with a "casual navigation" simulation from different location (London, New York, AWS data centre in Ireland) - 85 running instances (class F2) at peak - no errors reported other than random https sockets timeout - average response times - < 2s for gallery content navigation - < 1s for singe project page navitation - < 3s for static contend (loaded just once)
  • 34. 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 35.
  • 36.
  • 37.
  • 38. 1. Google App Engine (GAE) and AngularJS 2. data structures 3. assets management 4. load testing 5. GAE benchmarking tool and task inspector 6. GAE asynchronous API
  • 39. @classmethod def fix_dict(cls, dict, async=False): async_blobs = [] def _fix_dict(k, v): # blob if isinstance(v, blobstore.BlobKey): try: # create futures and put them apart, we'll resolve these later output = {'key': str(v), 'url': '', 'rpc': images.get_serving_url_async(v, secure_url=True)} async_blobs.append(output) return output except BaseException as e: # logging.warn('error while fetching serving url for [%s] maybe using corrupted image?' % (v,)) return None for k, v in dict.iteritems(): dict[k] = _fix_dict(k, v) if async is True: return dict, async_blobs else: cls.resolve_future_blobs(async_blobs) return dict @classmethod def resolve_future_blobs(cls, async_blobs): # resolve futures for blob in async_blobs: try: blob['url'] = blob['rpc'].get_result() except BaseException: blob['url'] = '' del(blob['rpc']) get_serving_url_async 1. get async url 3. get real url 2. resolve future blobs
  • 40. Thanks! uh, just few notes: 1. we are hiring! check www.stinkdigital.com/careers and send us your CV - careers@stinkdigital.com 2. we organise a Meetup in London with UNIT9 and B-Reel, get in touch! tomalisi@stinkdigital.com 3. www.slideshare.net/grudelsud