The magic behind your Lyft ride prices
A case study on machine learning and streaming
Strata Data, San Francisco, March 27th 2019
Rakesh Kumar | Engineer, Pricing
Thomas Weise | @thweise | Engineer, Streaming Platform
Agenda
2
Dynamic Pricing
Supply/Demand curve
ETA
Pricing
Notifications
Detect Delays
Coupons
User Delight
Fraud
Behaviour Fingerprinting
Monetary Impact
Imperative to act fast
Top Destinations
Core Experience
3
Introduction to Dynamic Pricing
4
What is prime time?
Location + time specific multiplier on the base fare for a ride
e.g. "in downtown SF at 5:00pm, prime time is 2.0"
Means we double the normal fare in that place at that time
Location: geohash6 (e.g. ‘9q8yyq’)
Time: calendar minute
5
Why do we need prime time?
6
Legacy Pricing Infrastructure
7
Legacy architecture: A series of cron jobs
SFO, calendar_min_1: {gh6: 1.0, gh6: 2.0, ...}
NYC: calendar_min_1: {gh6, 2.0, gh6: 1.0, ...}
8
Problems
9
Can we use Flink?
10
Streaming Stack
11
Streaming
Application
(SQL, Java)
Stream / Schema
Registry
Deployment
Tooling
Metrics & Dashboards
Alerts
Logging
Amazon
EC2
Amazon S3
Wavefront
Salt
(Config / Orca)
Docker
Source
Sink
11
Streaming and Python
12
Solution with Beam
Streaming
Application
(Python/Beam)
Source
Sink
13
Streaming based Pricing Infrastructure
14
Pipeline (conceptual outline)
15
kinesis events (source)
aggregate and window
filter events
run models to generate features (culminating in PT)
internal services
redis
ride_requested, app_open, ...
unique_users_per_min,
unique_requests_per_5_min, ...
conversion learner,
eta learner, ...
Lyft apps (phones)
valid sessions, dedupe, ...
Details of implementation
16
Gains
17
Beam and multiple languages
18
The Beam Vision
Beam Model: Fn Runners
Apache Flink
Apache Spark
Beam Model: Pipeline Construction
Other
Languages
Beam Java
Beam Python
Execution
Execution
Cloud Dataflow
Execution
19
Multi-Language Support
20
Python Example
p = beam.Pipeline(runner=runner, options=pipeline_options)
(p
| ReadFromText("/path/to/text*") | Map(lambda line: ...)
| WindowInto(FixedWindows(120)
trigger=AfterWatermark(� early=AfterProcessingTime(60),
late=AfterCount(1))� accumulation_mode=ACCUMULATING)
| CombinePerKey(sum))
| WriteToText("/path/to/outputs")
)
result = p.run()
( What, Where, When, How )
21
Portability (originally)
⋮
input | Sum.PerKey()
Python
input.apply(
Sum.integersPerKey())
Java
SELECT key, SUM(value) FROM input GROUP BY key
SQL (via Java)
⋮
Cloud Dataflow
Apache Spark
Apache Flink
Apache Apex
Gearpump
Apache Samza
Apache Nemo (incubating)
IBM Streams
Sum Per Key
Java objects
Sum Per Key
Dataflow JSON API
22
Portability (current)
⋮
input | Sum.PerKey()
Python
stats.Sum(s, input)
Go
SELECT key, SUM(value) FROM input GROUP BY key
SQL (via Java)
⋮
input.apply(
Sum.integersPerKey())
Java
Apache Spark
Apache Flink
Apache Apex
Gearpump
Cloud Dataflow
Apache Samza
Apache Nemo (incubating)
IBM Streams
Sum Per Key
Java objects
Sum Per Key
Portable protos
23
Beam Flink Runner
24
Portability Framework w/ Flink Runner
SDK
(Python)
Job Service
Artifact�Staging
Job Manager
Fn Services
(Beam Flink Task)
Task Manager
Executor / Fn API
Provision
Control
Data
Artifact�Retrieval
State
Logging
gRPC
Pipeline (protobuf)
Cluster
Runner
Dependencies
(optional)
python -m apache_beam.examples.wordcount \� --input=/etc/profile \� --output=/tmp/py-wordcount-direct \� --runner=PortableRunner \
--job_endpoint=localhost:8099 \� --streaming
Staging Location
(DFS, S3, …)
SDK Worker (UDFs)
SDK Worker (UDFs)
SDK Worker (Python)
Flink Job
25
Portable Runner
Common implementation for JVM based runners (/runners/java-fn-execution) and portable “Validate Runner” integration test suite in Python!
26
Fn API - Bundle Processing
Bundle size matters!
27
Lyft Flink Runner Customizations
28
How slow is this ?
Fn API
decode, …, window
count
(messages
| 'reshuffle' >> beam.Reshuffle()
| 'decode' >> beam.Map(lambda x: (__import__('random').randint(0, 511), 1))
| 'noop1' >> beam.Map(lambda x : x)
| 'noop2' >> beam.Map(lambda x : x)
| 'noop3' >> beam.Map(lambda x : x)
| 'window' >> beam.WindowInto(window.GlobalWindows(),
trigger=Repeatedly(AfterProcessingTime(5 * 1000)),
accumulation_mode= AccumulationMode.DISCARDING)
| 'group' >> beam.GroupByKey()
| 'count' >> beam.Map(count)
)
29
Fast enough for real Python work !
30
Beam Portability Recap
31
Feature Support Matrix (Beam 2.11.0)
32
Lessons Learned
33
Lessons Learned
34
We’re Hiring! Apply at www.lyft.com/careers
or email data-recruiting@lyft.com
Data Engineering
Engineering Manager
San Francisco
Software Engineer
San Francisco, Seattle, & New York City
Data Infrastructure
Engineering Manager
San Francisco
Software Engineer
San Francisco & Seattle
Experimentation
Software Engineer
San Francisco
Streaming
Software Engineer
San Francisco
Observability
Software Engineer
San Francisco
Please ask questions!
This presentation: