Serverless: budget-driven architecture
sli.do #Qeetup9SLS
@vithabada
@qest_developers
Qeetup vol. 9
Constraints we work with
- Cost of development, time to delivery, ...
- rapidly going down - cloud, better tooling & automation, many SaaS offerings
- Techstack
- rich buffet of cloud services vs. VMs running the thing built using a tech I happen to know
-
Cost of operation
- infrastructure elevated to a business concern by "serverless"
- => focus on impact of pay-per-request model
sli.do #Qeetup9SLS
Pricing review: Lambda
sli.do #Qeetup9SLS
Pricing review: DynamoDB
sli.do #Qeetup9SLS
On-Demand
Provisioned
WCU = 0.5 / 1kB
RCU = 4 / 8 kB
Two switchable billing modes:
Comparing costs
sli.do #Qeetup9SLS
- Avg 1 req / s (2629744 req / month)
- fits to free tier 3-8x
- Lambda invocation time <100ms
- DynamoDB write / reads <1kB / 8kB
Lambda 128MB | $1.07 |
Lambda 512MB | $2.72 |
DDB On-Demand | $2.63 |
DDB Provisioned | $0.00 |
t3a.nano 2vCPU/512MB | $3.67 |
Know your usage patterns!
Expected monthly cost without Free tier:
Lambda: need-to-knows to avoid injury
sli.do #Qeetup9SLS
- request life cycle
- queuing mechanism & auto-scaling
- package size & cold starts
- integration latency & concurrency
- ...and more, but not a focus of this talk
Lambda inside
REQ_A
container
REQ_B
executions
LATENCY_A
LATENCY_B
Notes:
- keep integ. latency low to minimize avg. concurrency
- Lambda does not like to wait too long for previous requests to complete
Package size & cold starts
- Cold start = lambda spinning up an container instance
- fetch code from S3
- unpack
- load environment
-
50MB zipped package size limit, 250MB unzipped
- 5MB package ... 0.8 - 1s
- 10MB package ... 1 - 1.2s
- => keep your Lambda package as small as possible
sli.do #Qeetup9SLS
Refactoring code
sli.do #Qeetup9SLS
for (...) {
await request();
}
await Promise.all(requests);
N * req_latency
req_latency
=> "Best practice" becomes a factor-N cost-saving measure
Refactoring infrastructure
sli.do #Qeetup9SLS
Example: Propagating change in data to a slow third party / legacy service
~1000ms
~100ms
10 req/s
10 [requests per second] * 12 [100ms increments] * price_per_100ms
Monthly: $262
Refactoring infrastructure
sli.do #Qeetup9SLS
Example: Propagating change in data to a slow third party / legacy service
N ~1000ms
~100ms
10 req/s
(10 [requests per second] * 1 [100ms increments] * price_per_100ms) + (num_batches * 11 [100ms increments])
Monthly: $21.9 (new BE cost) + $0.08 (5min batches)
λ
batch
Serverless Economics
sli.do #Qeetup9SLS
Pay-per-request pricing model impacts the way we code & the way we design our applications.
What about impact on business decisions, strategy, ... ?
Example
sli.do #Qeetup9SLS
+num_req * C_ratio * profit_per_C
Assume feature / project usage (num_req) is connected to the revenue stream with some conversion ratio of C_ratio yielding profit_per_C on average:
-num_req * price_per_req
Preferably:
(num_req * C_ratio * profit_per_C) — (num_req * price_per_req) > 0
Example
sli.do #Qeetup9SLS
Preferably:
(num_req * C_ratio * profit_per_C) — (num_req * price_per_req) > 0
Min. conversion ratio to make money:
C_ratio > price_per_req / profit_per_C
Note: no need to know usage beforehand to evaluate viability (or wait & hope for scale to kick in).
Note: number of requests (and possibly conversion rate) are actually functions of time.
Example
sli.do #Qeetup9SLS
profit(t) = (num_req(t) * C_ratio * profit_per_C) — (num_req(t) * price_per_req)
Profit as a function of time:
0 = total_expends — (num_req(t) * C_ratio * profit_per_C) — (num_req(t) * price_per_req)
Break even:
t_0
t_1
Time to break even:
t_1 — t_0
Note: use expected value from a sampled distribution instead of averages / estimates, if you have the data.
Go crazy, together
sli.do #Qeetup9SLS
Tighter cooperation between business and developers was always a good idea.
Thank you!
any Qestions?
Serverless: budget-driven architecture
By Vít Habada
Serverless: budget-driven architecture
- 912