Skip to content

Commit

Permalink
chore(examples): update python examples (#3769)
Browse files Browse the repository at this point in the history
* chore(examples): update python examples

* update python docs

* fix

* Update examples/language-sdk-instrumentation/python/simple/README.md

Co-authored-by: Christian Simon <simon@swine.de>

* Update examples/language-sdk-instrumentation/python/rideshare/django/README.md

Co-authored-by: Christian Simon <simon@swine.de>

* update doc links

* update screenshots and links

---------

Co-authored-by: Christian Simon <simon@swine.de>
  • Loading branch information
kolesnikovae and simonswine authored Dec 13, 2024
1 parent c7de7b7 commit 74a05a9
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 308 deletions.
4 changes: 2 additions & 2 deletions docs/sources/configure-client/language-sdks/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,5 @@ If your Pyroscope server has multi-tenancy enabled, you'll need to configure a t
## Python profiling examples

Check out the following resources to learn more about Python profiling:
- [Python examples](https://github.com/pyroscope-io/pyroscope/tree/main/examples/language-sdk-instrumentation/python)
- [Python demo](https://play.grafana.org/a/grafana-pyroscope-app/single?query=process_cpu%3Asamples%3Acount%3A%3Amilliseconds%7Bservice_name%3D%22pyroscope-rideshare-python%22%7D&from=now-1h&until=now) showing Python example with tags
- [Python examples](https://github.com/pyroscope-io/pyroscope/tree/main/examples/language-sdk-instrumentation/python) demonstrating how Django, Flask, and FastAPI apps can be profiled with Pyroscope.
- A [Python demo](https://play.grafana.org/a/grafana-pyroscope-app/profiles-explorer?searchText=&panelType=time-series&layout=grid&hideNoData=off&explorationType=flame-graph&var-serviceName=pyroscope-rideshare-python&var-profileMetricId=process_cpu:cpu:nanoseconds:cpu:nanoseconds&var-dataSource=grafanacloud-profiles) on play.grafana.org.
73 changes: 47 additions & 26 deletions examples/language-sdk-instrumentation/python/README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,57 @@
## Continuous Profiling for Python applications

### Profiling a Python Rideshare App with Pyroscope
![python_example_architecture_05_00](https://user-images.githubusercontent.com/23323466/135728737-0c5e54ca-1e78-4c6d-933c-145f441c96a9.gif)

![python_example_architecture_new_00](https://user-images.githubusercontent.com/23323466/173369382-267af200-6126-4bd0-8607-a933e8400dbb.gif)

#### _Read this in other languages._

<kbd>[简体中文](README_zh.md)</kbd>

Note: For documentation on the Pyroscope pip package visit [our website](https://pyroscope.io/docs/python/)
Note: For documentation on the Pyroscope pip package visit [our website](https://grafana.com/docs/pyroscope/latest/configure-client/language-sdks/python/)

## Live Demo

Feel free to check out the [live demo](https://play.grafana.org/a/grafana-pyroscope-app/profiles-explorer?searchText=&panelType=time-series&layout=grid&hideNoData=off&explorationType=flame-graph&var-serviceName=pyroscope-rideshare-python&var-profileMetricId=process_cpu:cpu:nanoseconds:cpu:nanoseconds&var-dataSource=grafanacloud-profiles) of this example on our demo page.

## Background

In this example we show a simplified, basic use case of Pyroscope. We simulate a "ride share" company which has three endpoints found in `server.py`:

- `/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 scooter

We also simulate running 3 distinct servers in 3 different regions (via [docker-compose.yml](https://github.com/pyroscope-io/pyroscope/blob/main/examples/python/docker-compose.yml))
We also simulate running 3 distinct servers in 3 different regions:

- us-east
- eu-north
- ap-south

One of the most useful capabilities of Pyroscope is the ability to tag your data in a way that is meaningful to you. In this case, we have two natural divisions, and so we "tag" our data to represent those:

- `region`: statically tags the region of the server running the code
- `vehicle`: dynamically tags the endpoint (similar to how one might tag a controller rails)


## Tagging static region

Tagging something static, like the `region`, can be done in the initialization code in the `config.tags` variable:
```

```python
pyroscope.configure(
application_name = "ride-sharing-app",
server_address = "http://pyroscope:4040",
tags = {
server_address = "http://pyroscope:4040",
tags = {
"region": f'{os.getenv("REGION")}', # Tags the region based off the environment variable
}
)
```

## Tagging dynamically within functions

Tagging something more dynamically, like we do for the `vehicle` tag can be done inside our utility `find_nearest_vehicle()` function using a `with pyroscope.tag_wrapper()` block
```

```python
def find_nearest_vehicle(n, vehicle):
with pyroscope.tag_wrapper({ "vehicle": vehicle}):
i = 0
Expand All @@ -46,58 +61,63 @@ def find_nearest_vehicle(n, vehicle):
```

What this block does, is:

1. Add the tag `{ "vehicle" => "car" }`
2. execute the `find_nearest_vehicle()` function
3. Before the block ends it will (behind the scenes) remove the `{ "vehicle" => "car" }` from the application since that block is complete

## Resulting flame graph / performance results from the example

### Running the example
To run the example run the following commands:
```

Try out one of the Django, Flask, or FastAPI examples located in the `rideshare` directory by running the following commands:

```shell
# Pull latest pyroscope and grafana images:
docker pull grafana/pyroscope:latest
docker pull grafana/grafana:latest

# Run the example project:
docker-compose up --build
docker compose up --build

# Reset the database (if needed):
# docker-compose down
docker compose down
```

What this example will do is run all the code mentioned above and also send some mock-load to the 3 servers as well as their respective 3 endpoints. If you select our application: `ride-sharing-app.cpu` from the dropdown, you should see a flame graph that looks like this (below). After we give 20-30 seconds for the flame graph to update and then click the refresh button we see our 3 functions at the bottom of the flame graph taking CPU resources _proportional to the size_ of their respective `search_radius` parameters.
What this example will do is run all the code mentioned above and also send some mock-load to the 3 servers as well as their respective 3 endpoints. If you select our application: `ride-sharing-app` from the dropdown, you should see a flame graph that looks like this (below). After we give 20-30 seconds for the flame graph to update and then click the refresh button we see our 3 functions at the bottom of the flame graph taking CPU resources _proportional to the size_ of their respective `search_radius` parameters.

## Where's the performance bottleneck?
![python_first_slide_05](https://user-images.githubusercontent.com/23323466/135881284-c75a5b65-6151-44fb-a459-c1f9559cb51a.jpg)

![python_slide_1](https://github.com/user-attachments/assets/1d38ddbf-2a9e-4f07-8d70-343cff878307)

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. In this case, it happens to be the `order_car` function.

The benefit of using the Pyroscope package, is that now that we can investigate further as to _why_ the `order_car()` function is problematic. Tagging both `region` and `vehicle` allows us to test two good hypotheses:
The benefit of using the Pyroscope package, is that now that we can investigate further as to _why_ the `order_car` function is problematic. Tagging both `region` and `vehicle` allows us to test two good hypotheses:
- Something is wrong with the `/car` endpoint code
- Something is wrong with one of our regions

To analyze this we can select one or more tags from the "Select Tag" dropdown:
To analyze this we can select one or more labels on the "Labels" page:

![image](https://user-images.githubusercontent.com/23323466/135525308-b81e87b0-6ffb-4ef0-a6bf-3338483d0fc4.png)
![python_slide_2](https://github.com/user-attachments/assets/5a8ee6ed-d2e1-42f3-98f3-d977adfccd08)

## Narrowing in on the Issue Using Tags
Knowing there is an issue with the `order_car()` function we automatically select that tag. Then, after inspecting multiple `region` tags, it becomes clear by looking at the timeline that there is an issue with the `eu-north` region, where it alternates between high-cpu times and low-cpu times.
## Narrowing in on the Issue Using Labels

We can also see that the `mutex_lock()` function is consuming almost 70% of CPU resources during this time period.
![python_second_slide_05](https://user-images.githubusercontent.com/23323466/135805908-ae9a1650-51fc-457a-8c47-0b56e8538b08.jpg)
Knowing there is an issue with the `order_car` function we automatically select that tag. Then, after inspecting multiple `region` tags, it becomes clear by looking at the timeline that there is an issue with the `eu-north` region, where it alternates between high-cpu times and low-cpu times.

## Comparing two time periods
Using Pyroscope's "comparison view" we can actually select two different time ranges from the timeline to compare the resulting flame graphs. The pink section on the left timeline results in the left flame graph, and the blue section on the right represents the right flame graph.
We can also see that the `find_nearest_vehicle` function is consuming almost 70% of CPU resources during this time period.

When we select a period of low-cpu utilization and a period of high-cpu utilization we can see that there is clearly different behavior in the `mutex_lock()` function where it takes **51% of CPU** during low-cpu times and **78% of CPU** during high-cpu times.
![python_third_slide_05](https://user-images.githubusercontent.com/23323466/135805969-55fdee40-fe0c-412d-9ec0-0bbc6a748ed4.jpg)
![python_slide_3](https://github.com/user-attachments/assets/57614064-bced-4363-bdba-b028c132e1e9)

## Visualizing diff between two flame graphs

While the difference _in this case_ is stark enough to see in the comparison view, sometimes the diff between the two flame graphs is better visualized with them overlayed over each other. Without changing any parameters, we can simply select the diff view tab and see the difference represented in a color-coded diff flame graph.
![python_fourth_slide_05](https://user-images.githubusercontent.com/23323466/135805986-594ffa3b-e735-4f91-875d-4f76fdff2b60.jpg)

![python_slide_4](https://github.com/user-attachments/assets/9c89458f-f7fb-4561-80a2-6a86c7c2ed4c)

### More use cases

We have been beta testing this feature with several different companies and some of the ways that we've seen companies tag their performance data:
- Linking profiles with trace data
- Tagging controllers
- Tagging regions
- Tagging jobs from a redis / sidekiq / rabbitmq queue
Expand All @@ -107,6 +127,7 @@ We have been beta testing this feature with several different companies and some
- Etc...

### Future Roadmap

We would love for you to try out this example and see what ways you can adapt this to your python application. Continuous profiling has become an increasingly popular tool for the monitoring and debugging of performance issues (arguably the fourth pillar of observability).

We'd love to continue to improve this pip package by adding things like integrations with popular tools, memory profiling, etc. and we would love to hear what features _you would like to see_.
6 changes: 3 additions & 3 deletions examples/language-sdk-instrumentation/python/README_zh.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
### Pyroscope Rideshare 示例
![python_example_architecture_05_00](https://user-images.githubusercontent.com/23323466/135728737-0c5e54ca-1e78-4c6d-933c-145f441c96a9.gif)
![python_example_architecture_new_00](https://user-images.githubusercontent.com/23323466/173369382-267af200-6126-4bd0-8607-a933e8400dbb.gif)

#### _用其他语言阅读此文。_
<kbd>[English](README.md)</kbd>

注意:关于Pyroscope pip包的文档,请访问[我们的网站](https://pyroscope.io/docs/python/)
注意:关于Pyroscope pip包的文档,请访问[我们的网站](https://grafana.com/docs/pyroscope/latest/configure-client/language-sdks/python/)
## 背景介绍

在这个例子中,我们展示了 Pyroscope 的一个简化的基本用例。我们模拟了一个 "骑行共享" 公司,它有三个请求端点,可以在`server.py`中找到:
Expand Down Expand Up @@ -53,7 +53,7 @@ def find_nearest_vehicle(n, vehicle):
### 运行这个例子
要运行该例子,请运行以下命令:
```
# 拉取最新的 pyroscope/grafana 镜像:
# 拉取最新的 pyroscope/pyroscope 镜像:
docker pull grafana/pyroscope:latest
docker pull grafana/grafana:latest
Expand Down
138 changes: 0 additions & 138 deletions examples/language-sdk-instrumentation/python/rideshare/README.md

This file was deleted.

Loading

0 comments on commit 74a05a9

Please sign in to comment.