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
    1. fetch code from S3
    2. unpack
    3. 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?

Made with Slides.com