From 4a2455bf1c84609fc59594c91da1708501922b68 Mon Sep 17 00:00:00 2001 From: Ali Pourhabibi Date: Thu, 22 Aug 2024 18:48:24 +0330 Subject: [PATCH 1/5] feat: add rwmutex routes Add rwmutex routes which use RWLock/Unlock for read operations --- api/v2/echo.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/api/v2/echo.go b/api/v2/echo.go index 3a46f4f..813ca35 100644 --- a/api/v2/echo.go +++ b/api/v2/echo.go @@ -22,7 +22,7 @@ type ListEntity struct { type server struct { list *linkedlist.LinkedList - mutex sync.Mutex + mutex sync.RWMutex } type customValidator struct { @@ -79,6 +79,9 @@ func V2() (*echo.Echo, error) { e.GET("/numbers/value/:value", s.Find) e.GET("/numbers/index/:index", s.Get) + e.GET("/numbers/rwmutex/value/:value", s.RWMutexFind) + e.GET("/numbers/rwmutex/index/:index", s.RWMutexGet) + return e, nil } @@ -168,3 +171,50 @@ func (s *server) Get(c echo.Context) error { c.JSON(http.StatusOK, data) return nil } + +func (s *server) RWMutexFind(c echo.Context) error { + valueStr := c.Param("value") + value, err := strconv.Atoi(valueStr) + if err != nil { + fmt.Println(err) + return echo.NewHTTPError(echo.ErrBadRequest.Code, "Invalid value") + } + + s.mutex.RLock() + defer s.mutex.RUnlock() + + index, ok := s.list.Find(value) + if !ok { + return echo.NewHTTPError(echo.ErrNotFound.Code, "Value not found") + } + + data := ListEntity{ + Index: index, + Value: value, + } + c.JSON(http.StatusOK, data) + return nil +} + +func (s *server) RWMutexGet(c echo.Context) error { + indexStr := c.Param("index") + index, err := strconv.ParseUint(indexStr, 10, 32) + if err != nil { + return echo.NewHTTPError(echo.ErrBadRequest.Code, "Invalid index") + } + + s.mutex.RLock() + defer s.mutex.RUnlock() + + value, ok := s.list.Get(uint(index)) + if !ok { + return echo.NewHTTPError(echo.ErrNotFound.Code, "Index not found") + } + data := ListEntity{ + Index: uint(index), + Value: value, + } + + c.JSON(http.StatusOK, data) + return nil +} From 7c6f7c5ef4a78ceefe20de4ea5e3ae5745529ef9 Mon Sep 17 00:00:00 2001 From: Ali Pourhabibi Date: Thu, 22 Aug 2024 18:49:11 +0330 Subject: [PATCH 2/5] bench: change benchmarks to handle rwmtex and concurrent benchmarking --- benchmark.sh | 59 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/benchmark.sh b/benchmark.sh index 1b9b20f..2d581e5 100755 --- a/benchmark.sh +++ b/benchmark.sh @@ -6,6 +6,7 @@ cleanup() { [ -e "$FIND_FILE" ] && unlink "$FIND_FILE" [ -e "post.lua" ] && unlink "post.lua" [ -e "get.lua" ] && unlink "get.lua" + [ -e "rwget.lua" ] && unlink "rwget.lua" echo "Cleanup completed" } @@ -54,13 +55,6 @@ request = function() end EOF -# Benchmark the insert operation -echo "Benchmarking insert operation..." -if ! wrk -t12 -c100 -d30s -s post.lua http://localhost:8080; then - echo "Error fibding on line $line" - exit 1 -fi - # Create a script for wrk to use for GET requests cat < get.lua wrk.method = "GET" @@ -77,10 +71,55 @@ request = function() end EOF +# Create a script for wrk to use for GET requests for RWMutex +cat < rwget.lua +wrk.method = "GET" +values = {} +index = 1 +$(awk '{print "table.insert(values, \"" $0 "\")"}' $FIND_FILE) +request = function() + local path = "/v2/numbers/rwmutex/value/" .. values[index] + index = index + 1 + if index > #values then + index = 1 + end + return wrk.format(nil, path) +end +EOF + +# Benchmark the insert operation +echo "Benchmarking insert operation..." +wrk -t12 -c100 -d30s -s post.lua http://localhost:8080 & +pid1=$! + # Benchmark the find operation echo "Benchmarking find operation..." -if ! wrk -t12 -c100 -d30s -s get.lua http://localhost:8080; then - exit 1 +wrk -t12 -c100 -d30s -s get.lua http://localhost:8080 & +pid2=$! + +# Benchmark the RWMutex find operation +echo "Benchmarking RWMutex find operation..." +wrk -t12 -c100 -d30s -s rwget.lua http://localhost:8080 & +pid3=$! + +# Wait for all background processes to finish +wait $pid1 $pid2 $pid3 + +# Check the exit statuses and exit with an error if any command failed +if [ $status1 -ne 0 ]; then + echo "Error: Benchmarking insert operation failed" + exit 1 fi -echo "completed successfully" \ No newline at end of file +if [ $status2 -ne 0 ]; then + echo "Error: Benchmarking find operation failed" + exit 1 +fi + +if [ $status3 -ne 0 ]; then + echo "Error: Benchmarking RWMutex find operation failed" + exit 1 +fi + +echo "Completed successfully" + From 2c187041e0554a0e1e49c39cd350887e7dfc8bea Mon Sep 17 00:00:00 2001 From: Ali Pourhabibi Date: Thu, 22 Aug 2024 18:50:02 +0330 Subject: [PATCH 3/5] chore: seperate routes for grafana dashboard --- .../provisioning/dashboards/my_dashboard.json | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/grafana/provisioning/dashboards/my_dashboard.json b/grafana/provisioning/dashboards/my_dashboard.json index 5c25fef..e969a87 100644 --- a/grafana/provisioning/dashboards/my_dashboard.json +++ b/grafana/provisioning/dashboards/my_dashboard.json @@ -12,41 +12,77 @@ "title": "HTTP Requests", "datasource": "Prometheus", "targets": [ + { - "expr": "histogram_quantile(0.50, sum(rate(myapp_request_duration_seconds_bucket[5m])) by (le))", - "legendFormat": "p50", - "refId": "A", + "expr": "histogram_quantile(0.50, sum(rate(myapp_request_duration_seconds_bucket{method=\"POST\", url=\"/numbers/:index/:value\"}[5m])) by (le))", + "legendFormat": "p50 - POST /numbers/:index/:value", "editorMode": "code", "range": true }, { - "expr": "histogram_quantile(0.90, sum(rate(myapp_request_duration_seconds_bucket[5m])) by (le))", - "legendFormat": "p90", - "refId": "B", + "expr": "histogram_quantile(0.50, sum(rate(myapp_request_duration_seconds_bucket{method=\"GET\", url=\"/numbers/value/:value\"}[5m])) by (le))", + "legendFormat": "p50 - GET /numbers/value/:value", "editorMode": "code", "range": true }, { - "expr": "histogram_quantile(0.99, sum(rate(myapp_request_duration_seconds_bucket[5m])) by (le))", - "legendFormat": "p99", - "refId": "C", + "expr": "histogram_quantile(0.50, sum(rate(myapp_request_duration_seconds_bucket{method=\"GET\", url=\"/numbers/rwmutex/value/:value\"}[5m])) by (le))", + "legendFormat": "p50 - GET /numbers/rwmutex/value/:value", + "editorMode": "code", + "range": true + }, + + { + "expr": "histogram_quantile(0.90, sum(rate(myapp_request_duration_seconds_bucket{method=\"POST\", url=\"/numbers/:index/:value\"}[5m])) by (le))", + "legendFormat": "p90 - POST /numbers/:index/:value", "editorMode": "code", "range": true }, { + "expr": "histogram_quantile(0.90, sum(rate(myapp_request_duration_seconds_bucket{method=\"GET\", url=\"/numbers/value/:value\"}[5m])) by (le))", + "legendFormat": "p90 - GET /numbers/value/:value", + "editorMode": "code", + "range": true + }, + { + "expr": "histogram_quantile(0.90, sum(rate(myapp_request_duration_seconds_bucket{method=\"GET\", url=\"/numbers/rwmutex/value/:value\"}[5m])) by (le))", + "legendFormat": "p90 - GET /numbers/rwmutex/value/:value", + "editorMode": "code", + "range": true + }, + + { + "expr": "histogram_quantile(0.95, sum(rate(myapp_request_duration_seconds_bucket{method=\"POST\", url=\"/numbers/:index/:value\"}[5m])) by (le))", + "legendFormat": "p95 - POST /numbers/:index/:value", + "editorMode": "code", + "range": true + }, + { + "expr": "histogram_quantile(0.95, sum(rate(myapp_request_duration_seconds_bucket{method=\"GET\", url=\"/numbers/value/:value\"}[5m])) by (le))", + "legendFormat": "p95 - GET /numbers/value/:value", + "editorMode": "code", + "range": true + }, + { + "expr": "histogram_quantile(0.95, sum(rate(myapp_request_duration_seconds_bucket{method=\"GET\", url=\"/numbers/rwmutex/value/:value\"}[5m])) by (le))", + "legendFormat": "p95 - GET /numbers/rwmutex/value/:value", + "editorMode": "code", + "range": true + }, + + { "expr": "sum(rate(myapp_request_duration_seconds_count[5m]))", "legendFormat": "Total Requests", - "refId": "D", "editorMode": "builder", "range": true }, { "expr": "sum(myapp_request_duration_seconds_sum)", "legendFormat": "Sum Duration", - "refId": "E", "editorMode": "builder", "range": true } + ], "fieldConfig": { "defaults": { @@ -67,7 +103,7 @@ } ] }, - "unit": "short" + "unit": "ms" }, "overrides": [] }, @@ -89,7 +125,7 @@ }, "yaxes": [ { - "format": "short", + "format": "ms", "show": true }, { @@ -100,3 +136,4 @@ } ] } + From b3bd8405a1d80e42fe74912e1b51f5c949c3df31 Mon Sep 17 00:00:00 2001 From: Ali Pourhabibi Date: Thu, 22 Aug 2024 19:05:37 +0330 Subject: [PATCH 4/5] test: add hrul tests for rwmutex searches --- hurl-tests/tests.hurl | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hurl-tests/tests.hurl b/hurl-tests/tests.hurl index 5743062..b6561d7 100644 --- a/hurl-tests/tests.hurl +++ b/hurl-tests/tests.hurl @@ -79,22 +79,46 @@ HTTP 200 jsonpath "$.index" == 0 jsonpath "$.value" == 2 +GET http://{{host}}/v2/numbers/rwmutex/index/0 +HTTP 200 +[Asserts] +jsonpath "$.index" == 0 +jsonpath "$.value" == 2 + GET http://{{host}}/v2/numbers/index/2 HTTP 404 [Asserts] jsonpath "$.message" == "Index not found" +GET http://{{host}}/v2/numbers/rwmutex/index/2 +HTTP 404 +[Asserts] +jsonpath "$.message" == "Index not found" + GET http://{{host}}/v2/numbers/value/4 HTTP 200 [Asserts] jsonpath "$.index" == 1 jsonpath "$.value" == 4 +GET http://{{host}}/v2/numbers/rwmutex/value/4 +HTTP 200 +[Asserts] +jsonpath "$.index" == 1 +jsonpath "$.value" == 4 + + GET http://{{host}}/v2/numbers/value/6 HTTP 404 [Asserts] jsonpath "$.message" == "Value not found" +GET http://{{host}}/v2/numbers/rwmutex/value/6 +HTTP 404 +[Asserts] +jsonpath "$.message" == "Value not found" + + DELETE http://{{host}}/v2/numbers/1 HTTP 200 @@ -102,3 +126,8 @@ GET http://{{host}}/v2/numbers/index/1 HTTP 404 [Asserts] jsonpath "$.message" == "Index not found" + +GET http://{{host}}/v2/numbers/rwmutex/index/1 +HTTP 404 +[Asserts] +jsonpath "$.message" == "Index not found" From f98fd15487b31c517714e413beda5d8fb4cd73ec Mon Sep 17 00:00:00 2001 From: Ali Pourhabibi Date: Sat, 24 Aug 2024 18:17:15 +0330 Subject: [PATCH 5/5] ref: handle lock/unlock better Handle Unlocking right after finish working with list --- api/v2/echo.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/api/v2/echo.go b/api/v2/echo.go index 813ca35..0ae4253 100644 --- a/api/v2/echo.go +++ b/api/v2/echo.go @@ -96,9 +96,9 @@ func (s *server) Insert(c echo.Context) error { } s.mutex.Lock() - defer s.mutex.Unlock() - ok := s.list.Insert(data.Index, data.Value) + s.mutex.Unlock() + if !ok { return echo.NewHTTPError(echo.ErrBadRequest.Code, "Invalid index") } @@ -114,9 +114,9 @@ func (s *server) Remove(c echo.Context) error { } s.mutex.Lock() - defer s.mutex.Unlock() - ok := s.list.Remove(uint(index)) + s.mutex.Unlock() + if !ok { return echo.NewHTTPError(echo.ErrNotFound.Code, "Index not found") } @@ -134,9 +134,9 @@ func (s *server) Find(c echo.Context) error { } s.mutex.Lock() - defer s.mutex.Unlock() - index, ok := s.list.Find(value) + s.mutex.Unlock() + if !ok { return echo.NewHTTPError(echo.ErrNotFound.Code, "Value not found") } @@ -157,9 +157,9 @@ func (s *server) Get(c echo.Context) error { } s.mutex.Lock() - defer s.mutex.Unlock() - value, ok := s.list.Get(uint(index)) + s.mutex.Unlock() + if !ok { return echo.NewHTTPError(echo.ErrNotFound.Code, "Index not found") } @@ -181,9 +181,9 @@ func (s *server) RWMutexFind(c echo.Context) error { } s.mutex.RLock() - defer s.mutex.RUnlock() - index, ok := s.list.Find(value) + s.mutex.RUnlock() + if !ok { return echo.NewHTTPError(echo.ErrNotFound.Code, "Value not found") } @@ -204,9 +204,9 @@ func (s *server) RWMutexGet(c echo.Context) error { } s.mutex.RLock() - defer s.mutex.RUnlock() - value, ok := s.list.Get(uint(index)) + s.mutex.RUnlock() + if !ok { return echo.NewHTTPError(echo.ErrNotFound.Code, "Index not found") }