Skip to content

Commit 9ad1893

Browse files
authored
[Feature] Add proper Prometheus endpoint compression (#1393)
1 parent f551be0 commit 9ad1893

File tree

14 files changed

+743
-49
lines changed

14 files changed

+743
-49
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
- (Bugfix) Fix Member Terminating state discovery
1111
- (Bugfix) Fix CRD yaml (chart)
1212
- (Bugfix) (EE) Fix MemberMaintenance Context and ClusterMaintenance discovery
13-
-
13+
- (Feature) Add proper Prometheus endpoint compression + 204 response code
14+
1415
## [1.2.32](https://github.com/arangodb/kube-arangodb/tree/1.2.32) (2023-08-07)
1516
- (Feature) Backup lifetime - remove Backup once its lifetime has been reached
1617
- (Feature) Add Feature dependency

pkg/deployment/features/local.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030

3131
"github.com/arangodb/go-driver"
3232

33-
"github.com/arangodb/kube-arangodb/pkg/logging"
3433
"github.com/arangodb/kube-arangodb/pkg/util"
3534
)
3635

@@ -120,54 +119,9 @@ func Init(cmd *cobra.Command) error {
120119

121120
f.StringVar(&configMapName, "features-config-map-name", DefaultFeaturesConfigMap, "Name of the Feature Map ConfigMap")
122121

123-
checkDependencies(cmd)
124-
125122
return nil
126123
}
127124

128-
func checkDependencies(cmd *cobra.Command) {
129-
130-
enableDeps := func(_ *cobra.Command, _ []string) {
131-
// Turn on dependencies. This function will be called when all process's arguments are passed, so
132-
// all required features are enabled and dependencies should be enabled too.
133-
EnableDependencies()
134-
135-
// Log enabled features when process starts.
136-
for _, f := range features {
137-
if !f.Enabled() {
138-
continue
139-
}
140-
141-
l := logging.Global().RegisterAndGetLogger("features", logging.Info)
142-
if deps := f.GetDependencies(); len(deps) > 0 {
143-
l = l.Strs("dependencies", deps...)
144-
}
145-
146-
l.Bool("enterpriseArangoDBRequired", f.EnterpriseRequired()).
147-
Str("minArangoDBVersion", string(f.Version())).
148-
Str("name", f.Name()).
149-
Info("feature enabled")
150-
}
151-
}
152-
153-
// Wrap pre-run function if it set.
154-
if cmd.PreRunE != nil {
155-
local := cmd.PreRunE
156-
cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
157-
enableDeps(cmd, args)
158-
return local(cmd, args)
159-
}
160-
} else if cmd.PreRun != nil {
161-
local := cmd.PreRun
162-
cmd.PreRun = func(cmd *cobra.Command, args []string) {
163-
enableDeps(cmd, args)
164-
local(cmd, args)
165-
}
166-
} else {
167-
cmd.PreRun = enableDeps
168-
}
169-
}
170-
171125
func cmdRun(_ *cobra.Command, _ []string) {
172126
featuresLock.Lock()
173127
defer featuresLock.Unlock()

pkg/server/server.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929

3030
"github.com/gin-gonic/gin"
3131
"github.com/jessevdk/go-assets"
32-
prometheus "github.com/prometheus/client_golang/prometheus/promhttp"
3332
core "k8s.io/api/core/v1"
3433
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
3534
typedCore "k8s.io/client-go/kubernetes/typed/core/v1"
@@ -39,6 +38,7 @@ import (
3938
"github.com/arangodb/kube-arangodb/dashboard"
4039
"github.com/arangodb/kube-arangodb/pkg/util/errors"
4140
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
41+
"github.com/arangodb/kube-arangodb/pkg/util/metrics"
4242
"github.com/arangodb/kube-arangodb/pkg/util/probe"
4343
"github.com/arangodb/kube-arangodb/pkg/version"
4444
)
@@ -177,7 +177,7 @@ func NewServer(cli typedCore.CoreV1Interface, cfg Config, deps Dependencies) (*S
177177
readyProbes = append(readyProbes, deps.Storage.Probe)
178178
}
179179
r.GET("/ready", gin.WrapF(ready(readyProbes...)))
180-
r.GET("/metrics", gin.WrapH(prometheus.Handler()))
180+
r.GET("/metrics", gin.WrapF(metrics.Handler()))
181181
r.POST("/login", s.auth.handleLogin)
182182
api := r.Group("/api", s.auth.checkAuthentication)
183183
{

pkg/util/http/buffer.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2023 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package http
22+
23+
import (
24+
"bytes"
25+
"fmt"
26+
"io"
27+
"net/http"
28+
"sync"
29+
30+
"github.com/arangodb/kube-arangodb/pkg/util"
31+
)
32+
33+
const (
34+
ContentLengthHeader = "Content-Length"
35+
)
36+
37+
func WithBuffer(maxSize int, in http.HandlerFunc) http.HandlerFunc {
38+
return func(writer http.ResponseWriter, request *http.Request) {
39+
data := NewBuffer(maxSize, writer)
40+
41+
wr := NewWriter(writer, data)
42+
43+
in(wr, request)
44+
45+
if !data.Truncated() {
46+
// We have constant size
47+
bytes := data.Bytes()
48+
49+
println(len(bytes))
50+
51+
writer.Header().Set(ContentLengthHeader, fmt.Sprintf("%d", len(bytes)))
52+
53+
_, err := util.WriteAll(writer, bytes)
54+
if err != nil {
55+
logger.Err(err).Warn("Unable to write HTTP response")
56+
}
57+
}
58+
}
59+
}
60+
61+
type Buffer interface {
62+
io.Writer
63+
64+
Bytes() []byte
65+
Truncated() bool
66+
}
67+
68+
type buffer struct {
69+
lock sync.Mutex
70+
71+
upstream io.Writer
72+
73+
data, currentData []byte
74+
}
75+
76+
func (b *buffer) Write(q []byte) (n int, err error) {
77+
b.lock.Lock()
78+
defer b.lock.Unlock()
79+
80+
p := q
81+
82+
for {
83+
if len(p) == 0 || len(b.currentData) == 0 {
84+
break
85+
}
86+
87+
b.currentData[0] = p[0]
88+
b.currentData = b.currentData[1:]
89+
p = p[1:]
90+
}
91+
92+
if len(p) == 0 {
93+
return len(q), nil
94+
}
95+
96+
written := 0
97+
98+
if len(b.currentData) == 0 {
99+
if b.data != nil {
100+
z, err := util.WriteAll(b.upstream, b.data)
101+
if err != nil {
102+
return 0, err
103+
}
104+
105+
written += z
106+
107+
b.data = nil
108+
b.currentData = nil
109+
}
110+
} else {
111+
return len(q), nil
112+
}
113+
114+
z, err := b.upstream.Write(p)
115+
if err != nil {
116+
return 0, err
117+
}
118+
119+
written += z
120+
121+
return written, nil
122+
}
123+
124+
func (b *buffer) Bytes() []byte {
125+
b.lock.Lock()
126+
defer b.lock.Unlock()
127+
128+
if len(b.data) == 0 {
129+
return nil
130+
}
131+
132+
return b.data[:len(b.data)-len(b.currentData)]
133+
}
134+
135+
func (b *buffer) Truncated() bool {
136+
b.lock.Lock()
137+
defer b.lock.Unlock()
138+
139+
return len(b.data) == 0
140+
}
141+
142+
type bytesBuffer struct {
143+
*bytes.Buffer
144+
}
145+
146+
func (b bytesBuffer) Truncated() bool {
147+
return false
148+
}
149+
150+
func NewBuffer(maxSize int, upstream io.Writer) Buffer {
151+
if maxSize <= 0 {
152+
return &bytesBuffer{
153+
Buffer: bytes.NewBuffer(nil),
154+
}
155+
}
156+
157+
b := &buffer{
158+
data: make([]byte, maxSize),
159+
upstream: upstream,
160+
}
161+
b.currentData = b.data
162+
return b
163+
}

pkg/util/http/buffer_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2023 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package http
22+
23+
import (
24+
"bytes"
25+
"testing"
26+
27+
"github.com/stretchr/testify/require"
28+
29+
"github.com/arangodb/kube-arangodb/pkg/util"
30+
)
31+
32+
func Test_Buffer(t *testing.T) {
33+
t.Run("Normal cache", func(t *testing.T) {
34+
up := bytes.NewBuffer(nil)
35+
b := NewBuffer(4096, up)
36+
37+
data := make([]byte, 1024)
38+
39+
c, err := util.WriteAll(b, data)
40+
require.Len(t, data, c)
41+
42+
require.NoError(t, err)
43+
44+
require.Len(t, up.Bytes(), 0)
45+
require.Len(t, b.Bytes(), 1024)
46+
require.False(t, b.Truncated())
47+
})
48+
t.Run("Full cache", func(t *testing.T) {
49+
up := bytes.NewBuffer(nil)
50+
b := NewBuffer(1024, up)
51+
52+
data := make([]byte, 1024)
53+
54+
c, err := util.WriteAll(b, data)
55+
require.Len(t, data, c)
56+
57+
require.NoError(t, err)
58+
59+
require.Len(t, up.Bytes(), 0)
60+
require.Len(t, b.Bytes(), 1024)
61+
require.False(t, b.Truncated())
62+
})
63+
t.Run("Full cache + 1", func(t *testing.T) {
64+
up := bytes.NewBuffer(nil)
65+
b := NewBuffer(1024, up)
66+
67+
data := make([]byte, 1025)
68+
69+
c, err := util.WriteAll(b, data)
70+
require.Len(t, data, c)
71+
72+
require.NoError(t, err)
73+
74+
require.Len(t, up.Bytes(), 1025)
75+
require.Len(t, b.Bytes(), 0)
76+
require.True(t, b.Truncated())
77+
})
78+
t.Run("Overflow cache", func(t *testing.T) {
79+
up := bytes.NewBuffer(nil)
80+
b := NewBuffer(1024, up)
81+
82+
data := make([]byte, 2048)
83+
84+
c, err := util.WriteAll(b, data)
85+
require.Len(t, data, c)
86+
87+
require.NoError(t, err)
88+
89+
require.Len(t, up.Bytes(), 2048)
90+
require.Len(t, b.Bytes(), 0)
91+
require.True(t, b.Truncated())
92+
})
93+
}

pkg/util/http/content.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2023 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package http
22+
23+
import "net/http"
24+
25+
const ContentTypeHeader = "Content-Type"
26+
27+
func WithTextContentType(in http.HandlerFunc) http.HandlerFunc {
28+
return WithContentType("plain/text", in)
29+
}
30+
31+
func WithContentType(content string, in http.HandlerFunc) http.HandlerFunc {
32+
return func(writer http.ResponseWriter, request *http.Request) {
33+
writer.Header().Set(ContentTypeHeader, content)
34+
35+
in(writer, request)
36+
}
37+
}

0 commit comments

Comments
 (0)