https://github.com/xpetit/fizzbuzz
A 500k requests per second fizzbuzz server, using the standard library.
https://github.com/xpetit/fizzbuzz
benchmark fizzbuzz fizzbuzz-go go golang performance
Last synced: about 1 year ago
JSON representation
A 500k requests per second fizzbuzz server, using the standard library.
- Host: GitHub
- URL: https://github.com/xpetit/fizzbuzz
- Owner: xpetit
- License: mit
- Created: 2022-01-14T14:11:51.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2023-08-07T13:29:19.000Z (almost 3 years ago)
- Last Synced: 2025-06-20T21:21:51.969Z (about 1 year ago)
- Topics: benchmark, fizzbuzz, fizzbuzz-go, go, golang, performance
- Language: Go
- Homepage: https://pkg.go.dev/github.com/xpetit/fizzbuzz/v5
- Size: 167 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# FizzBuzz
FizzBuzz is a Golang HTTP server exposing a RESTful web API that provides a [Fizz buzz](https://en.wikipedia.org/wiki/Fizz_buzz) service.
It has two endpoints:
- `/api/v2/fizzbuzz`
- Accepts five optional query parameters : three integers `int1`, `int2` and `limit`, and two strings `str1` and `str2`.
The default values are: `limit=10`, `int1=2`, `int2=3`, `str1=fizz`, `str2=buzz`.
- Returns a list of strings with numbers from 1 to `limit`, where: all multiples of `int1` are replaced by `str1`, all multiples of `int2` are replaced by `str2`, all multiples of `int1` and `int2` are replaced by `str1str2`.
- `/api/v2/fizzbuzz/stats`
- Accept no parameters
- Return the parameters corresponding to the most used request, as well as the number of hits for this request
The server is:
- Ready for production
- Easy to maintain by other developers
## Usage
Requirements:
- [Go 1.20 or newer](https://golang.org/dl/)
Use this command to directly update and run the service:
```
go run github.com/xpetit/fizzbuzz/v5/cmd/fizzbuzzd@latest
```
To stop it, type CTRL-C.
If you don't trust this program, you can use Docker. Clone this repository and run the following commands inside:
```
docker build --tag github.com/xpetit/fizzbuzz/v5 .
docker run --rm --publish 8080:8080 github.com/xpetit/fizzbuzz/v5
```
When the service is running, you can query with it with `curl`:
```
curl localhost:8080/api/v2/fizzbuzz/stats
```
>
> ```json
> {"most_frequent":{"count":0}}
> ```
Using the defaults:
```
curl localhost:8080/api/v2/fizzbuzz
```
>
> ```json
> ["1","fizz","buzz","fizz","5","fizzbuzz","7","fizz","buzz","fizz"]
> ```
The `config` object is now populated:
```
curl localhost:8080/api/v2/fizzbuzz/stats
```
>
> ```json
> {"most_frequent":{"count":1,"config":{"limit":10,"int1":2,"int2":3,"str1":"fizz","str2":"buzz"}}}
> ```
Custom request:
```
curl localhost:8080/api/v2/fizzbuzz -Gdlimit=1 -dint1=1 -dint2=1 -dstr1=buzz -dstr2=lightyear
```
> ```json
> ["buzzlightyear"]
> ```
## Design
In writing this library, several considerations were taken into account:
1. Should it provide a generalized, extensible implementation that supports any number of "int" and "str"?
2. Should it provide a FizzBuzz function that not only writes values but returns values?
1. Should the returned values be a slice of JSON string or a more compact (binary) representation for later reuse?
This was discarded for the following reasons:
1. If requirements change, it is easy to adapt the Fizz buzz algorithm because it is simple.
2. A library that streams JSON strings and one that generates them all at once are a very different story. One cannot be the generalized/abstract version of another.

### Packages
The top-down list of dependencies is as follows:
- `github.com/xpetit/fizzbuzz/v5/cmd/fizzbuzzd`: The main program, running the HTTP server.
- `github.com/xpetit/fizzbuzz/v5/handlers`: The HTTP handlers.
- `github.com/xpetit/fizzbuzz/v5/stats`: The statistics services.
- `github.com/xpetit/fizzbuzz/v5`: The Fizz buzz writer `WriteTo`.
### Performance
Benchmarks performed on an AWS EC2 instance (c6i.4xlarge) with the following specs:
| | |
| --- | -------------------------------------- |
| CPU | Intel Xeon Platinum 8375C CPU @2.90GHz |
| RAM | 32 GB ECC 2666 MHz |
| Go | Version 1.20.5 |
The programs were compiled with `GOAMD64=v4` and ran with `GOGC=1000` environment variables.
`WriteTo` limits memory allocation, here are the results from 0 to 10 million values of Fizz buzz:
```
BenchmarkWriteTo/[limit:0e+00]-16 271018197 4.431 ns/op 677.05 MB/s 0 B/op 0 allocs/op
BenchmarkWriteTo/[limit:1e+00]-16 11806252 99.79 ns/op 60.13 MB/s 32 B/op 5 allocs/op
BenchmarkWriteTo/[limit:1e+01]-16 5970025 200.2 ns/op 334.67 MB/s 48 B/op 6 allocs/op
BenchmarkWriteTo/[limit:1e+02]-16 1000000 1016 ns/op 685.71 MB/s 48 B/op 6 allocs/op
BenchmarkWriteTo/[limit:1e+03]-16 114363 10508 ns/op 694.45 MB/s 48 B/op 6 allocs/op
BenchmarkWriteTo/[limit:1e+04]-16 10000 106170 ns/op 718.63 MB/s 48 B/op 6 allocs/op
BenchmarkWriteTo/[limit:1e+05]-16 1074 1113399 ns/op 715.19 MB/s 48 B/op 6 allocs/op
BenchmarkWriteTo/[limit:1e+06]-16 100 11305590 ns/op 733.82 MB/s 48 B/op 6 allocs/op
BenchmarkWriteTo/[limit:1e+07]-16 9 117342031 ns/op 735.43 MB/s 49 B/op 6 allocs/op
```
The service stops writing values as soon as the API consumer no longer requests them.
[`wrk`](https://github.com/wg/wrk) reports 447k requests/second with a random limit between 1 and 100 (using `-db off` command-line argument):
```
Running 10s test @ http://127.0.0.1:8080
5 threads and 600 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.45ms 1.85ms 47.51ms 90.65%
Req/Sec 90.68k 7.72k 111.89k 68.94%
4511934 requests in 10.10s, 1.86GB read
Requests/sec: 446918.14
Transfer/sec: 188.49MB
```
Leaving the database enabled:
```
Running 10s test @ http://127.0.0.1:8080
4 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 39.17ms 70.20ms 770.02ms 88.86%
Req/Sec 7.81k 636.24 10.35k 76.25%
310796 requests in 10.02s, 131.31MB read
Requests/sec: 31002.22
Transfer/sec: 13.10MB
```
And with `-db :memory:` command-line argument ([SQLite in-memory DB](https://www.sqlite.org/inmemorydb.html)):
```
Running 10s test @ http://127.0.0.1:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 242.48us 189.82us 2.21ms 86.00%
Req/Sec 22.80k 635.84 24.37k 65.84%
458245 requests in 10.10s, 193.35MB read
Requests/sec: 45371.08
Transfer/sec: 19.14MB
```