Skip to content

Commit 8ffa3c2

Browse files
committed
chore: updated examples
1 parent eed1eeb commit 8ffa3c2

File tree

12 files changed

+509
-192
lines changed

12 files changed

+509
-192
lines changed

examples/routine_leak/example2/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ naked code of `step1` to the full integration in the last step.
66

77
The application we are going to use for this demonstration is very simple:
88

9-
1. The `main` function starts the `doJob` go routine and then, every 4 seconds, prints the `pproof` goroutine dump
10-
2. `doJob` runs indefinitely and every 50 milliseconds invokes the `foo` function
11-
3. `foo` just invokes `bar` wich in turn starts the `parentGoRoutine` routine
12-
4. `parentGoRoutine` starts the `doInterestingStuff` routine passing a random number between 0 and 99
13-
5. `doInterestingStuff` hangs indefinitely if the received value is a multiple of 4, otherwise exits
9+
1. The `main` function repeatedly starts cycles where it processes multiple websites.
10+
For each cycle, randomly selects 10 website URLs from a predefined list.
11+
For each selected URL, asynchronously invokes the `getWebsiteResponseSize`
12+
2. The `getWebsiteResponseSize` asynchronously calls the `getResponseSize` and do some fun stuff with the result
13+
3. The `getResponseSize` contacts the site and returns the response size
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package data
2+
3+
var Websites = []string{
4+
"https://google.com",
5+
"https://facebook.com",
6+
"https://youtube.com",
7+
"https://amazon.com",
8+
"https://wikipedia.com",
9+
"https://instagram.com",
10+
"https://linkedin.com",
11+
"https://reddit.com",
12+
"https://ebay.com",
13+
"https://microsoft.com",
14+
"https://apple.com",
15+
"https://walmart.com",
16+
"https://espn-bad.com",
17+
"https://bbc.com",
18+
"https://cnn.com",
19+
"https://foxnews.com",
20+
"https://nytimes.com",
21+
"https://forbes.com",
22+
"https://cnbc.com",
23+
"https://theguardian.com",
24+
"https://nbcnews.com",
25+
"https://abc.com",
26+
"https://time.com",
27+
"https://nationalgeographic.com",
28+
"https://wired-bad.com",
29+
"https://techcrunch.com",
30+
"https://engadget.com",
31+
"https://gizmodo.com",
32+
"https://mashable.com",
33+
"https://stackoverflow.com",
34+
"https://github.com",
35+
"https://medium-bad.com",
36+
"https://dropbox.com",
37+
"https://box.com",
38+
"https://slack.com",
39+
"https://zoom.com",
40+
"https://stripe.com",
41+
"https://airbnb.com",
42+
"https://booking.com",
43+
"https://hotels-bad.com",
44+
"https://kayak.com",
45+
"https://monster.com",
46+
"https://edx.com",
47+
"https://duolingo-bad.com",
48+
"https://britannica-bad.com",
49+
"https://dictionary.com",
50+
"https://merriam-webster.com",
51+
"https://thesaurus.com",
52+
"https://weather-bad.com",
53+
"https://trulia-bad.com",
54+
"https://epicurious.com",
55+
"https://tasteofhome.com",
56+
"https://healthline.com",
57+
"https://webmd.com",
58+
"https://mayoclinic-bad.com",
59+
"https://everydayhealth.com",
60+
"https://livestrong.com",
61+
"https://fool-bad.com",
62+
"https://seekingalpha.com",
63+
"https://nerdwallet.com",
64+
"https://bankrate.com",
65+
"https://creditkarma.com",
66+
"https://mint.com",
67+
"https://fidelity.com",
68+
"https://vanguard.com",
69+
}

examples/routine_leak/example2/step1/README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
In this step, we will use the native `go` keyword to start go routines.
44
The code is very simple:
55

6-
1. The `main` function starts the `doJob` go routine and then, every 4 seconds, prints the `pproof` goroutine dump
7-
2. `doJob` runs indefinitely and every 50 milliseconds invokes the `foo` function
8-
3. `foo` just invokes `bar` wich in turn starts the `parentGoRoutine` routine
9-
4. `parentGoRoutine` starts the `doInterestingStuff` routine passing a random number between 0 and 99
10-
5. `doInterestingStuff` hangs indefinitely if the received value is a multiple of 4, otherwise exits
6+
1. The `main` function repeatedly starts cycles where it processes multiple websites.
7+
For each cycle, randomly selects 10 website URLs from a predefined list.
8+
For each selected URL, asynchronously invokes the `getWebsiteResponseSize`
9+
2. The `getWebsiteResponseSize` asynchronously calls the `getResponseSize` and do some fun stuff with the result
10+
3. The `getResponseSize` contacts the site and returns the response size
1111

1212
Running the application we can see how the total number of goroutine keeps increasing:
1313
```
14-
goroutine profile: total 32
15-
20 @ 0x100e9caa8 0x100e7d520 0x100ef588c 0x100ea42b4
16-
# 0x100ef588b main.doInterestingStuff+0x3b
14+
goroutine profile: total 18
15+
14 @ 0x102d71768 0x102d361a8 0x102d70a10 0x102db7a38 0x102db830c 0x102db82fd 0x102e451d8 0x102e4d974 0x102e87430 0x102dcd820 0x102e87610 0x102e84dac 0x102e8ad44 0x102e8ad45 0x102eb83b4 0x102d9d940 0x102ee5b18 0x102ee5aed 0x102ee6148 0x102ef6b50 0x102ef60c8 0x102d79aa4
16+
# 0x102d70a0f internal/poll.runtime_pollWait+0x9f
1717
...
1818
...
19-
goroutine profile: total 50
20-
38 @ 0x100e9caa8 0x100e7d520 0x100ef588c 0x100ea42b4
21-
# 0x100ef588b main.doInterestingStuff+0x3b
19+
goroutine profile: total 33
20+
26 @ 0x102d71768 0x102d361a8 0x102d70a10 0x102db7a38 0x102db830c 0x102db82fd 0x102e451d8 0x102e4d974 0x102e87430 0x102dcd820 0x102e87610 0x102e84dac 0x102e8ad44 0x102e8ad45 0x102eb83b4 0x102d9d940 0x102ee5b18 0x102ee5aed 0x102ee6148 0x102ef6b50 0x102ef60c8 0x102d79aa4
21+
# 0x102d70a0f internal/poll.runtime_pollWait+0x9f
2222
```
2323

24-
We can see that the leaked routine os the `doInterestingStuff` but we have no idea of the context of execution.
24+
Understanding which routine we are leaking is not immediate.
Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,71 @@
11
package main
22

33
import (
4+
"fmt"
5+
"github.com/openshift-online/async-routine/examples/routine_leak/example2/data"
6+
"io"
47
"math/rand"
8+
"net/http"
59
"os"
610
"runtime/pprof"
11+
"strconv"
712
"time"
813
)
914

1015
func main() {
11-
go doJob()
12-
1316
for {
14-
time.Sleep(4 * time.Second)
17+
for i := 0; i < 10; i++ {
18+
url := data.Websites[rand.Intn(len(data.Websites))]
19+
go doJob(url)
20+
time.Sleep(500 * time.Millisecond)
21+
}
1522
_ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
1623
}
17-
1824
}
1925

20-
func doJob() {
21-
for {
22-
foo()
23-
time.Sleep(50 * time.Millisecond)
24-
}
26+
func doJob(url string) {
27+
resultChan := make(chan int64)
28+
go getResponseSize(url, resultChan)
29+
size := <-resultChan
30+
// do something fun with the size - here we just avoid the compilation error
31+
size = size
2532
}
2633

27-
func foo() {
28-
bar()
29-
}
34+
// getResponseSize fetches the given URL and sends the response size (in bytes) to the provided channel.
35+
// Returns an error if the site does not exist or the request fails.
36+
func getResponseSize(url string, ch chan<- int64) error {
37+
// Perform the HTTP request
38+
res, err := http.Get(url)
39+
if err != nil {
40+
return fmt.Errorf("site unreachable: %w", err)
41+
}
42+
defer res.Body.Close()
3043

31-
func bar() {
32-
go parentGoroutine()
33-
}
44+
// Handle HTTP errors
45+
if res.StatusCode != http.StatusOK {
46+
switch res.StatusCode {
47+
case http.StatusNotFound:
48+
return fmt.Errorf("site does not exist (404 Not Found)")
49+
default:
50+
return fmt.Errorf("invalid HTTP response: %d %s", res.StatusCode, http.StatusText(res.StatusCode))
51+
}
52+
}
3453

35-
func parentGoroutine() {
36-
go doInterestingStuff(rand.Intn(100))
37-
time.Sleep(500 * time.Millisecond)
38-
}
54+
// Try to use Content-Length header if available
55+
if contentLength := res.Header.Get("Content-Length"); contentLength != "" {
56+
size, err := strconv.ParseInt(contentLength, 10, 64)
57+
if err == nil && size > 0 {
58+
ch <- size
59+
return nil
60+
}
61+
}
3962

40-
func doInterestingStuff(value int) {
41-
if value%4 == 0 {
42-
select {}
63+
// Calculate the size by reading the response body
64+
body, err := io.ReadAll(res.Body)
65+
if err != nil {
66+
return fmt.Errorf("failed to read response body: %w", err)
4367
}
68+
69+
ch <- int64(len(body))
70+
return nil
4471
}

examples/routine_leak/example2/step2/app_leaking_routine.go

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,83 @@ package main
22

33
import (
44
"context"
5+
"fmt"
56
"github.com/openshift-online/async-routine"
7+
"github.com/openshift-online/async-routine/examples/routine_leak/example2/data"
8+
"io"
69
"math/rand"
10+
"net/http"
711
"os"
812
"runtime/pprof"
13+
"strconv"
914
"time"
1015
)
1116

1217
func main() {
13-
async.
14-
NewAsyncRoutine("main-job", context.Background(), doJob).
15-
Run()
16-
1718
for {
18-
time.Sleep(4 * time.Second)
19+
for i := 0; i < 10; i++ {
20+
url := data.Websites[rand.Intn(len(data.Websites))]
21+
async.NewAsyncRoutine(
22+
"do-job",
23+
context.Background(),
24+
func() {
25+
doJob(url)
26+
}).
27+
Run()
28+
time.Sleep(500 * time.Millisecond)
29+
}
1930
_ = pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
2031
}
21-
2232
}
2333

24-
func doJob() {
25-
for {
26-
foo()
27-
time.Sleep(50 * time.Millisecond)
28-
}
34+
func doJob(url string) {
35+
resultChan := make(chan int64)
36+
async.NewAsyncRoutine(
37+
"get-website-size",
38+
context.Background(),
39+
func() {
40+
getResponseSize(url, resultChan)
41+
}).Run()
42+
size := <-resultChan
43+
// do something fun with the size - here we just avoid the compilation error
44+
size = size
2945
}
3046

31-
func foo() {
32-
bar()
33-
}
47+
// GetResponseSize fetches the given URL and sends the response size (in bytes) to the provided channel.
48+
// Returns an error if the site does not exist or the request fails.
49+
func getResponseSize(url string, ch chan<- int64) error {
50+
// Perform the HTTP request
51+
res, err := http.Get(url)
52+
if err != nil {
53+
return fmt.Errorf("site unreachable: %w", err)
54+
}
55+
defer res.Body.Close()
3456

35-
func bar() {
36-
async.
37-
NewAsyncRoutine("parent-go-routine", context.Background(), parentGoroutine).
38-
Run()
39-
}
57+
// Handle HTTP errors
58+
if res.StatusCode != http.StatusOK {
59+
switch res.StatusCode {
60+
case http.StatusNotFound:
61+
return fmt.Errorf("site does not exist (404 Not Found)")
62+
default:
63+
return fmt.Errorf("invalid HTTP response: %d %s", res.StatusCode, http.StatusText(res.StatusCode))
64+
}
65+
}
4066

41-
func parentGoroutine() {
42-
async.
43-
NewAsyncRoutine("run-command", context.Background(), func() { doInterestingStuff(rand.Intn(100)) }).
44-
Run()
45-
time.Sleep(500 * time.Millisecond)
46-
}
67+
// Try to use Content-Length header if available
68+
if contentLength := res.Header.Get("Content-Length"); contentLength != "" {
69+
size, err := strconv.ParseInt(contentLength, 10, 64)
70+
if err == nil && size > 0 {
71+
ch <- size
72+
return nil
73+
}
74+
}
4775

48-
func doInterestingStuff(value int) {
49-
if value%4 == 0 {
50-
select {}
76+
// Calculate the size by reading the response body
77+
body, err := io.ReadAll(res.Body)
78+
if err != nil {
79+
return fmt.Errorf("failed to read response body: %w", err)
5180
}
81+
82+
ch <- int64(len(body))
83+
return nil
5284
}

examples/routine_leak/example2/step3/README.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,29 @@ func main() {
2626

2727
The output will be something like this:
2828
```
29-
running routine count: 14
30-
running routine count: 17
31-
running routine count: 19
32-
running routine count: 21
33-
running routine count: 23
34-
running routine count: 28
35-
running routine count: 30
36-
running routine count: 31
37-
...
29+
running routine count: 2
30+
running routine count: 3
31+
running routine count: 3
32+
running routine count: 2
33+
running routine count: 4
34+
running routine count: 4
35+
running routine count: 2
36+
running routine count: 4
37+
running routine count: 4
38+
running routine count: 2
39+
running routine count: 3
40+
running routine count: 5
41+
running routine count: 3
42+
running routine count: 3
43+
running routine count: 5
44+
running routine count: 5
45+
running routine count: 3
46+
running routine count: 4
47+
running routine count: 4
48+
running routine count: 4
49+
running routine count: 5
50+
running routine count: 5
51+
running routine count: 7
3852
```
3953

4054
We can see we have a routine leak, but we still have no idea of what is the routine leak.

0 commit comments

Comments
 (0)