KLAUS IHLBERG

TEAM LEAD • CONFLUENCE
Saving Money By Optimising
Your Cloud Infrastructure
PATRICK STREULE

ARCHITECT • ECOSYSTEM
• Big cool statistic
• 2,56
9
• Add-Ons in Marketplace
SERVERS
SERVERS EVERYWHERE
• Big cool statistic
• 2,56
9
• Add-Ons in Marketplace
STATIC ADD ONS
Agenda
STORING ADD ON DATA
STATIC ADD ONS
Agenda
STORING ADD ON DATA
STATIC ADD ONS
SERVERLESS ADD ONS
Agenda
Static Add-Ons
Static Site
Static Add-on
HTML
JS

CSS
HTML
JS

CSS
Browser
Static Hosting

CDN
Cheap
Fast
Cacheable
Static Site
Static Add-on
HTML
JS

CSS
HTML
JS

CSS
Browser
Static Hosting

CDN
Host
Static Site
Static Add-on
HTML
JS

CSS
HTML
JS

CSS
Browser
Static Hosting

CDN
Host
API
Iframe bridge
JS (and REST) API
Example:
Image Editor
Host Client API Lib
Adobe editor
Versioned add-on CSS
Versioned add-on JS
React Mount Point
Caching
<script src="//d2q4nobwyhnvov.cloudfront.net/8c9e5743ca3d/editor.js"></script>
Version 1
<script src="//d2q4nobwyhnvov.cloudfront.net/7a8h273ac3c1/editor.js"></script>
Version 2
Cache-Control: public, max-age=31536000
Tell browser to cache for a year
Cache-Control: public, max-age=31536000
New version hash: No cache hit
Lifecycle Hooks JWT Opt-Out
No Yes
installable": {
"callbackUrl": "https://addon…",
"allowRoom": true,
"allowGlobal": false
}
{
"key": "editor",
"title": {
"value": "Image editor"
},
"url": "https://image-editor…",
"authentication": "none"
},
No Webhooks

(or other server-side modules)
Scopes are still needed
No Yes
"webhook": {
"url": "https://addon…",
"event": "room_message",
"pattern": "something"
}
"hipchatApiConsumer": {
"scopes": [
"send_notification",
"view_room"
]
}
Free up to two add-onsIntegrated in BitbucketHosted on Aerobatic
Automatic asset
versioning, CDN, SSL
Git push deployments Yes, free!
Hosting
Serverless Data Storage
JSON
Properties
var property = {
key: "usage",
value: {
views: [
{
date: "2016-08-26T03:35:57.138Z",
views: 122,
},{
date: "2016-08-27T03:35:57.138Z",
views: 65,
}
],
users: [
{
fullName: "Klaus Ihlberg",
id: "kihlberg"
},
{
fullName: "Patrick Streule",
id: "pstreule"
}
]
},
};
var property = {
key: "usage",
value: {
views: [
{
date: "2016-08-26T03:35:57.138Z",
views: 122,
},{
date: "2016-08-27T03:35:57.138Z",
views: 65,
}
],
users: [
{
fullName: "Klaus Ihlberg",
id: "kihlberg"
},
{
fullName: "Patrick Streule",
id: "pstreule"
}
]
},
};
Property Key
var property = {
key: "usage",
value: {
views: [
{
date: "2016-08-26T03:35:57.138Z",
views: 122,
},{
date: "2016-08-27T03:35:57.138Z",
views: 65,
}
],
users: [
{
fullName: "Klaus Ihlberg",
id: "kihlberg"
},
{
fullName: "Patrick Streule",
id: "pstreule"
}
]
},
};
Value Object
var property = {
key: "usage",
value: {
views: [
{
date: "2016-08-26T03:35:57.138Z",
views: 122,
},{
date: "2016-08-27T03:35:57.138Z",
views: 65,
}
],
users: [
{
fullName: "Klaus Ihlberg",
id: "kihlberg"
},
{
fullName: "Patrick Streule",
id: "pstreule"
}
]
},
};
Array of Page Views
var property = {
key: "usage",
value: {
views: [
{
date: "2016-08-26T03:35:57.138Z",
views: 122,
},{
date: "2016-08-27T03:35:57.138Z",
views: 65,
}
],
users: [
{
fullName: "Klaus Ihlberg",
id: "kihlberg"
},
{
fullName: "Patrick Streule",
id: "pstreule"
}
]
},
};
Array of Users
Blueprint
Browser
Add-On
Aerobatic
iframe
Blueprint
Browser
RESTAPI
Add-On
Aerobatic
iframe
iFrameBridge
Blueprint
Browser
RESTAPI
Database
Add-On
Aerobatic
iframe
iFrameBridge
iFrameBridge
Add-On
AP.request()
Pages
Comments
Spaces
iFrameBridge
Add-On
AP.request()
POST /rest/api/space/{key}/property
Pages
Comments
Spaces
iFrameBridge
Add-On
AP.request()
POST /rest/api/content/{id}/property
Pages
Comments
Spaces
AP.request({
url: "/rest/api/content/" + contentId + “/property/usage",
success: function(response) {
var property = JSON.parse(response);
console.log(property);
}
});
Connect AP.request Module
AP.request({
url: "/rest/api/content/" + contentId + “/property/usage",
success: function(response) {
var property = JSON.parse(response);
console.log(property);
}
});
Content Property URL
with Content ID…
…and property
key
AP.request({
url: "/rest/api/content/" + contentId + “/property/usage",
success: function(response) {
var property = JSON.parse(response);
console.log(property);
}
});
Success function
GET /rest/api/content/{id}/property
GET /rest/api/space/{key}/property
GET /rest/api/2/issue/{id}/properties
GET /rest/api/2/project/{key}/properties
GET /rest/api/2/user/properties
GET /rest/atlassian-connect/1/addons/{add-on-key}/properties
32 kB size limit
…and you can have more than
one
Custom Content
Other Options for Data Storage
Custom Content Media API
Other Options for Data Storage
Serverless“ ”
Client-accessible
backend services.
Pay for what you use.
Run short-lived
backend code.
Don't pay for idle
time.
Backend as a
Service
Functions as a
Service
Functions as a Service
time
𝝺 𝝺𝝺
Invocation
Deployed, 

but not running
𝝺
Invocation
Started when 

called
𝝺
Stopped when

inactive
𝝺
May stay active

across multiple

calls
Elastic ScalingLess OpsNo Infrastructure Cheap Pre-prod
Benefits
No base image
updates, no instance
provisioning
Monitoring, deployment
infrastructure etc. still
needed. 

Serverless != NoOps
Low-traffic dev and
staging environments
don't add cost
Managed and
automatic
Dev
Staging
Production
Start-up penaltyExpensive at scaleTooling
Less mature tooling
for development,
deployment,
debugging, …
Possibly cheaper to
run instances and
containers at scale
Increased latency on
requests that spin up
the function
Downsides
Example 1
Example
Lambda
Functionality
AWS API GatewayAWS Lambda
Runs the code. 

Pay only for 

execution time
HTTP to Lambda
gateway. Pay for # of
calls and data
transfer.
What is being used?
Blueprint
/wolfram 1+1
APIGateway
𝝺
Implementation
webhook
webhook response
Wolfram Lambda - 8:12 PM
1+1: 2
Deployed to AWS
Test data
Business Logic
Test helper
API token config
Lambda entry point
Deployment
descriptor
Exclude files not
needed at runtime
Keep them as
low as possible
This is the
entry point POST /wolfram
This is the

entry point
Reply to the

HipChat

web hook
Call Wolfram

Alpha API
Transform

API Result
(removed error handling)
Monthly cost?
2000
calls/month
Free calls to 

Wolfram Alpha API
API responses
10KB
data per call+ = $0.01
Example 2
Example
ACTIVITY GRAPH
AWS DynamoDBAWS API GatewayAWS Lambda
Runs the code. 

Pay only for 

execution time.
HTTP to Lambda
gateway. 

Pay for # of calls and
data transfer.
Stores the data. 

Pay for storage and
access.
What is being used?
Aerobatic
Hosts static assets.

Free for two add-ons.
Blueprint
Browser
git push
APIGateway
𝝺
Implementation
DynamoDB
Aerobatic
iframe
webhook
data and assets
Two

endpoints:
- handle git push

- return data to UI
DynamoDB

Table Definition
Static page,

ajax call to 

/activity for 

data
Webhook called

on every push
Base URL,

replaced by

Aerobatic
Proxy calls

to Lambda
Add cache header
Environment variable to configure Lambda URL
Monthly cost?
100'000
views/day
One query 

per view
One write

per commit
≈$15+
20'000
commits/day
1KB
data size+
Counters

are small
Links
Wolfram Lambda Code: https://bitbucket.org/pstreule/wolfram-lambda
Commit Stats Code: https://bitbucket.org/pstreule/bb-activity-graph
Usage Tracking Code: https://bitbucket.org/kihlberg/usage-tracking
Thank you!
KLAUS IHLBERG

TEAM LEAD • CONFLUENCE

@TWITTERHANDLE
PATRICK STREULE

ARCHITECT • ECOSYSTEM

@PSTREULE

Saving Money by Optimizing Your Cloud Add-On Infrastructure