Skip to content

Commit 341cf45

Browse files
committed
Add more extensive benchmarking.
1 parent 20808a8 commit 341cf45

17 files changed

+626
-60
lines changed

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
SHELL := /bin/bash
44

55
GOFLAGS ?= -ldflags=\"-extldflags=-static\" $(GOFLAGS:)
6+
BENCHARGS ?= -run=NONE -bench=.
67

78
export KIEBITZ_TEST = yes
89

@@ -32,7 +33,7 @@ test-races: test-setup
3233
KIEBITZ_SETTINGS=$(KIEBITZ_TEST_SETTINGS) go test -race $(testargs) `go list ./...`
3334

3435
bench: test-setup
35-
KIEBITZ_SETTINGS=$(KIEBITZ_TEST_SETTINGS) go test -run=NONE -bench=. $(GOFLAGS) `go list ./... | grep -v api/`
36+
KIEBITZ_SETTINGS=$(KIEBITZ_TEST_SETTINGS) go test $(BENCHARGS) $(GOFLAGS) `go list ./... | grep -v api/`
3637

3738
clean:
3839
@go clean $(GOFLAGS) -i ./...

appointments.go

+23
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,29 @@ type SignedAppointment struct {
304304
PublicKey []byte `json:"publicKey"`
305305
}
306306

307+
func MakeAppointment(timestamp time.Time, slots, duration int64) (*Appointment, error) {
308+
if id, err := crypto.RandomBytes(32); err != nil {
309+
return nil, err
310+
} else {
311+
slotData := make([]*Slot, slots)
312+
for i, _ := range slotData {
313+
sd := &Slot{}
314+
if id, err := crypto.RandomBytes(32); err != nil {
315+
return nil, err
316+
} else {
317+
sd.ID = id
318+
}
319+
slotData[i] = sd
320+
}
321+
return &Appointment{
322+
Timestamp: timestamp,
323+
Duration: duration,
324+
ID: id,
325+
SlotData: slotData,
326+
}, nil
327+
}
328+
}
329+
307330
type Appointment struct {
308331
Timestamp time.Time `json:"timestamp"`
309332
Duration int64 `json:"duration"`

databases/redis.go

+8-23
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,16 @@ type RedisLock struct {
4949
dLock *redislock.Lock
5050
}
5151

52-
func MakeRedisLock(ctx context.Context, lockKey string, client redis.UniversalClient) (RedisLock, error) {
53-
54-
rL := RedisLock{
52+
func MakeRedisLock(ctx context.Context, lockKey string, client redis.UniversalClient) *RedisLock {
53+
return &RedisLock{
5554
client: client,
5655
lockKey: lockKey,
5756
ctx: ctx,
5857
dLockClient: redislock.New(client),
5958
}
60-
61-
return rL, nil
6259
}
6360

64-
// Makes sure, that RedisLock implements services.Lock
65-
var _ services.Lock = RedisLock{}
66-
67-
func (r RedisLock) Lock() error {
61+
func (r *RedisLock) Lock() error {
6862

6963
lock, err := r.dLockClient.Obtain(r.ctx, r.lockKey, 100*time.Millisecond, nil)
7064

@@ -76,9 +70,9 @@ func (r RedisLock) Lock() error {
7670
return nil
7771
}
7872

79-
func (r RedisLock) Release() error {
73+
func (r *RedisLock) Release() error {
8074
if r.dLock == nil {
81-
services.Log.Error("Unable to release lock, which was never locked.")
75+
services.Log.Errorf("Unable to release lock %s, which was never locked.", r.lockKey)
8276
return errors.New("no lock")
8377
}
8478

@@ -206,22 +200,13 @@ func (d *Redis) Reset() error {
206200

207201
func (d *Redis) Lock(lockKey string) (services.Lock, error) {
208202

209-
redisLock, err := MakeRedisLock(d.ctx, lockKey, d.client)
210-
211-
if err != nil {
212-
return nil, err
213-
}
214-
215-
err = redisLock.Lock()
203+
redisLock := MakeRedisLock(d.ctx, lockKey, d.client)
216204

217-
if err != nil {
205+
if err := redisLock.Lock(); err != nil {
218206
return nil, err
219207
}
220208

221-
var sLock services.Lock
222-
sLock = redisLock
223-
224-
return sLock, nil
209+
return redisLock, nil
225210

226211
}
227212

helpers/client.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ type Response struct {
4141
body []byte
4242
}
4343

44-
func (r *Response) read() error {
44+
func (r *Response) Read() error {
45+
4546
if r.body != nil {
4647
return nil // already read the body
4748
}
@@ -74,7 +75,7 @@ func (r *Response) CoerceResult(target interface{}) error {
7475
func (r *Response) JSON() (map[string]interface{}, error) {
7576
var value map[string]interface{}
7677

77-
if err := r.read(); err != nil {
78+
if err := r.Read(); err != nil {
7879
return nil, err
7980
}
8081

@@ -86,7 +87,7 @@ func (r *Response) JSON() (map[string]interface{}, error) {
8687
}
8788

8889
func (r *Response) Bytes() ([]byte, error) {
89-
if err := r.read(); err != nil {
90+
if err := r.Read(); err != nil {
9091
return nil, err
9192
}
9293
return r.body, nil
@@ -150,6 +151,10 @@ func Request(url, method string, params interface{}, key *crypto.Key, client *ht
150151

151152
req, err := http.NewRequest("POST", url, reader)
152153

154+
// this is important, Golang won't close requests otherwise...
155+
// https://github.com/golang/go/issues/28012
156+
req.Close = true
157+
153158
if err != nil {
154159
return nil, err
155160
}
@@ -159,7 +164,10 @@ func Request(url, method string, params interface{}, key *crypto.Key, client *ht
159164
if resp, err := client.Do(req); err != nil {
160165
return nil, err
161166
} else {
162-
return &Response{Response: resp}, nil
167+
response := &Response{Response: resp}
168+
// we always read the body as otherwise the connection can stay open
169+
// and will not be freed properly...
170+
return response, response.Read()
163171
}
164172

165173
}
@@ -260,7 +268,7 @@ func (a *AppointmentsClient) GetStats(params *services.GetStatsParams) (*Respons
260268
}
261269

262270
func (a *AppointmentsClient) GetAppointmentsByZipCode(params *services.GetAppointmentsByZipCodeParams) (*Response, error) {
263-
return nil, nil
271+
return a.requester("getAppointmentsByZipCode", params, nil)
264272
}
265273

266274
func (a *AppointmentsClient) GetProviderAppointments(params *services.GetProviderAppointmentsParams) (*Response, error) {

http/server.go

+66-12
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ package http
1818

1919
import (
2020
"context"
21+
cryptoTls "crypto/tls"
2122
"fmt"
2223
"github.com/kiebitz-oss/services"
2324
"github.com/kiebitz-oss/services/metrics"
25+
kiebitzNet "github.com/kiebitz-oss/services/net"
26+
"github.com/kiebitz-oss/services/tls"
2427
"github.com/prometheus/client_golang/prometheus"
28+
"net"
2529
"net/http"
2630
"net/url"
2731
"regexp"
@@ -48,10 +52,17 @@ type Route struct {
4852

4953
type H map[string]interface{}
5054

55+
type Hooks struct {
56+
Finished Handler
57+
}
58+
5159
type HTTPServer struct {
52-
settings *services.HTTPServerSettings
60+
settings *HTTPServerSettings
61+
tlsConfig *cryptoTls.Config
62+
listener net.Listener
5363
mutex sync.Mutex
5464
running bool
65+
hooks *Hooks
5566
err error
5667
server *http.Server
5768
routeGroups []*RouteGroup
@@ -81,7 +92,7 @@ func initializeRouteGroup(routeGroup *RouteGroup) error {
8192
return nil
8293
}
8394

84-
func MakeHTTPServer(settings *services.HTTPServerSettings, routeGroups []*RouteGroup, metricsPrefix string) (*HTTPServer, error) {
95+
func MakeHTTPServer(settings *HTTPServerSettings, routeGroups []*RouteGroup, metricsPrefix string) (*HTTPServer, error) {
8596

8697
for _, routeGroup := range routeGroups {
8798
if err := initializeRouteGroup(routeGroup); err != nil {
@@ -90,12 +101,19 @@ func MakeHTTPServer(settings *services.HTTPServerSettings, routeGroups []*RouteG
90101
}
91102

92103
s := &HTTPServer{
93-
metricsPrefix: metricsPrefix,
94104
settings: settings,
95105
routeGroups: routeGroups,
96106
mutex: sync.Mutex{},
107+
hooks: &Hooks{},
108+
metricsPrefix: metricsPrefix,
97109
server: &http.Server{
98110
Addr: settings.BindAddress,
111+
// we disable HTTP/2 for all servers as there seems to be a bug in the Golang
112+
// HTTP/2 implementation that causes EOF errors when reading from the server
113+
// response, which in turn causes trouble with our proxy server when terminating
114+
// This can be re-eneabled once the bug is fixed upstream...
115+
// more info: https://github.com/golang/go/issues/46071
116+
TLSNextProto: make(map[string]func(*http.Server, *cryptoTls.Conn, http.Handler)),
99117
},
100118
}
101119

@@ -105,6 +123,18 @@ func MakeHTTPServer(settings *services.HTTPServerSettings, routeGroups []*RouteG
105123
return s, nil
106124
}
107125

126+
func (h *HTTPServer) SetHooks(hooks *Hooks) {
127+
h.hooks = hooks
128+
}
129+
130+
func (h *HTTPServer) SetListener(listener net.Listener) {
131+
h.listener = listener
132+
}
133+
134+
func (h *HTTPServer) SetTLSConfig(config *cryptoTls.Config) {
135+
h.tlsConfig = config
136+
}
137+
108138
func handleRouteGroup(context *Context, group *RouteGroup, handlers []Handler) {
109139

110140
for i, route := range group.Routes {
@@ -123,14 +153,16 @@ func handleRouteGroup(context *Context, group *RouteGroup, handlers []Handler) {
123153
}
124154
}
125155
}
156+
if context.Aborted {
157+
break
158+
}
126159
}
127160

128161
for _, subgroup := range group.Subgroups {
129162
handleRouteGroup(context, subgroup, append(handlers, group.Handlers...))
130163
}
131164

132165
}
133-
134166
func (s *HTTPServer) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
135167

136168
startHandleTime := time.Now()
@@ -161,6 +193,7 @@ func (s *HTTPServer) ServeHTTP(writer http.ResponseWriter, request *http.Request
161193
}
162194

163195
s.httpDurations.WithLabelValues(u.Path, statusCodeString).Observe(handleDuration.Seconds())
196+
164197
}
165198

166199
func (s *HTTPServer) Start() error {
@@ -180,16 +213,37 @@ func (s *HTTPServer) Start() error {
180213

181214
var listener func() error
182215

183-
if s.settings.TLS != nil {
184-
// we listen via a TLS connection
185-
listener = func() error {
186-
return s.server.ListenAndServeTLS(s.settings.TLS.CertificateFile, s.settings.TLS.KeyFile)
216+
useTLS := false
217+
if s.settings.TLS != nil && s.tlsConfig == nil {
218+
219+
var err error
220+
s.tlsConfig, err = tls.TLSServerConfig(s.settings.TLS)
221+
222+
if err != nil {
223+
return err
224+
}
225+
}
226+
227+
if s.tlsConfig != nil {
228+
useTLS = true
229+
s.server.TLSConfig = s.tlsConfig
230+
}
231+
232+
if s.listener == nil {
233+
if listener, err := net.Listen("tcp", s.settings.BindAddress); err != nil {
234+
return err
235+
} else if s.settings.TCPRateLimits != nil {
236+
s.listener = kiebitzNet.MakeRateLimitedListener(listener, s.settings.TCPRateLimits)
237+
} else {
238+
s.listener = listener
187239
}
188-
} else {
189-
// we listen without TLS (e.g. when we're behind a reverse proxy with TLS termination)
190-
listener = func() error {
191-
return s.server.ListenAndServe()
240+
}
241+
242+
listener = func() error {
243+
if useTLS {
244+
return s.server.ServeTLS(s.listener, "", "")
192245
}
246+
return s.server.Serve(s.listener)
193247
}
194248

195249
go func() {

http/settings.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Kiebitz - Privacy-Friendly Appointment Scheduling
2+
// Copyright (C) 2021-2021 The Kiebitz Authors
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as
6+
// published by the Free Software Foundation, either version 3 of the
7+
// License, or (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package http
18+
19+
import (
20+
"github.com/kiebitz-oss/services"
21+
"github.com/kiebitz-oss/services/net"
22+
)
23+
24+
// Settings for the JSON-RPC server
25+
type HTTPServerSettings struct {
26+
TLS *services.TLSSettings `json:"tls"`
27+
BindAddress string `json:"bind_address"`
28+
TCPRateLimits []*net.RateLimit `json:"tcp_rate_limits"`
29+
}

jsonrpc/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func MakeJSONRPCServer(settings *services.JSONRPCServerSettings, handler Handler
110110
},
111111
}
112112

113-
httpServerSettings := &services.HTTPServerSettings{
113+
httpServerSettings := &http.HTTPServerSettings{
114114
TLS: settings.TLS,
115115
BindAddress: settings.BindAddress,
116116
}

0 commit comments

Comments
 (0)