docs/sources/get-started/ride-share-tutorial.md
This tutorial demonstrates a basic use case of Pyroscope by profiling a "Ride Share" application. In this example, you learn:
You need to have the following prerequisites to complete this tutorial:
{{< admonition type="tip" >}} Try this tutorial in an interactive learning environment: Ride share tutorial with Pyroscope.
It's a fully configured environment with all the dependencies installed.
Provide feedback, report bugs, and raise issues in the Grafana Killercoda repository. {{< /admonition >}}
<!-- INTERACTIVE ignore END -->In this tutorial, you will profile a simple "Ride Share" application. The application is a Python Flask app that simulates a ride-sharing service. The app has three endpoints which are found in the lib/server.py file:
/bike : calls the order_bike(search_radius) function to order a bike/car : calls the order_car(search_radius) function to order a car/scooter : calls the order_scooter(search_radius) function to order a scooterTo simulate a highly available and distributed system, the app is deployed on three distinct servers in 3 different regions:
This is simulated by running three instances of the server in Docker containers. Each server instance is tagged with the region it represents.
{{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-demo.gif" caption="Getting started sample application" alt="Getting started sample application" >}}
In this scenario, a load generator will send mock-load to the three servers as well as their respective endpoints. This lets you see how the application performs per region and per vehicle type.
{{<docs/ignore>}}
{{< admonition type="tip" >}}
A setup script runs in the background to install the necessary dependencies. This should take no longer than 30 seconds. Your instance will be ready to use once you Setup complete. You may now begin the tutorial.
{{< /admonition >}}
{{</docs/ignore>}}
Clone the repository to your local machine:
git clone https://github.com/grafana/pyroscope.git && cd pyroscope
Navigate to the tutorial directory:
cd examples/language-sdk-instrumentation/python/rideshare/flask
Start the application using Docker Compose:
docker compose up -d
This may take a few minutes to download the required images and build the demo application. Once ready, you will see the following output:
✔ Network flask_default Created
✔ Container flask-ap-south-1 Started
✔ Container flask-grafana-1 Started
✔ Container flask-pyroscope-1 Started
✔ Container flask-load-generator-1 Started
✔ Container flask-eu-north-1 Started
✔ Container flask-us-east-1 Started
Optional: To verify the containers are running, run:
docker ps -a
Grafana includes the Profiles Drilldown app that you can use to view profile data. To access Profiles Drilldown, open a browser and navigate to http://localhost:3000/a/grafana-pyroscope-app/profiles-explorer.
In this example, the application is instrumented with Pyroscope using the Python SDK. The SDK allows you to tag functions with metadata that can be used to filter and group the profile data in the Profiles Drilldown. This example uses static and dynamic tagging.
To start, let's take a look at a static tag use case. Within the lib/server.py file, find the Pyroscope configuration:
pyroscope.configure(
application_name = app_name,
server_address = server_addr,
basic_auth_username = basic_auth_username, # for grafana cloud
basic_auth_password = basic_auth_password, # for grafana cloud
tags = {
"region": f'{os.getenv("REGION")}',
}
)
This tag is considered static because the tag is set at the start of the application and doesn't change. In this case, it's useful for grouping profiles on a per region basis, which lets you see the performance of the application per region.
You should now see a list of regions that the application is running in. You can see that eu-north is experiencing the most load.
{{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-tag-region-2.png" caption="Region Tag" alt="Region Tag" >}}
Next, look at a dynamic tag use case. Within the lib/utility/utility.py file, find the following function:
def find_nearest_vehicle(n, vehicle):
with pyroscope.tag_wrapper({ "vehicle": vehicle}):
i = 0
start_time = time.time()
while time.time() - start_time < n:
i += 1
if vehicle == "car":
check_driver_availability(n)
This example uses tag_wrapper to tag the function with the vehicle type.
Notice that the tag is dynamic as it changes based on the vehicle type.
This is useful for grouping profiles on a per vehicle basis, allowing us to see the performance of the application per vehicle type being requested.
Use Profiles Drilldown to see how this tag is used:
You should now see a list of vehicle types that the application is using. You can see that car is experiencing the most load.
The first step when analyzing a profile outputted from your application, is to take note of the largest node which is where your application is spending the most resources. To discover this, you can use the Flame graph view:
ride-sharing-app is selected in the Service drop-down menu and process_cpu/cpu in the Profile type drop-down menu.It should look something like this:
{{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-bottle-neck-3.png" caption="Bottleneck" alt="Bottleneck" >}}
The flask dispatch_request function is the parent to three functions that correspond to the three endpoints of the application:
order_bikeorder_carorder_scooterBy tagging both region and vehicle and looking at the Labels view, you can hypothesize:
/car endpoint code where car vehicle tag is consuming 68% of CPUeu-north region tag is consuming 54% of CPUFrom the flame graph, you can see that for the eu-north tag the biggest performance impact comes from the find_nearest_vehicle() function which consumes close to 68% of cpu.
To analyze this, go directly to the comparison page using the comparison dropdown.
The Diff flame graph view lets you compare two time periods side by side.
This is useful for identifying changes in performance over time.
This example compares the performance of the eu-north region within a given time period against the other regions.
ride-sharing-app is selected in the Service drop-down menu and process_cpu/cpu in the Profile type drop-down menu.region and select != eu-north.region and select == eu-north.Scroll down to compare the two time periods side by side.
Note that the eu-north region (right side) shows an excessive amount of time spent in the find_nearest_vehicle function.
This looks to be caused by a mutex lock that is causing the function to block.
{{< figure max-width="100%" src="/media/docs/pyroscope/ride-share-time-comparison-2.png" caption="Time Comparison" alt="Time Comparison" >}}
<!-- INTERACTIVE page step3.md END --> <!-- INTERACTIVE page step4.md START -->The docker-compose.yml file includes a Grafana container that's pre-configured with the Pyroscope plugin:
grafana:
image: grafana/grafana:latest
environment:
- GF_PLUGINS_PREINSTALL_SYNC=grafana-pyroscope-app
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
volumes:
- ./grafana-provisioning:/etc/grafana/provisioning
ports:
- 3000:3000
Grafana is also pre-configured with the Pyroscope data source.
As a challenge, see if you can generate a similar comparison with the vehicle tag.
In this tutorial, you learned how to profile a simple "Ride Share" application using Pyroscope. You have learned some of the core instrumentation concepts such as tagging and how to use Profiles Drilldown identify performance bottlenecks.