Skip to content

Commit 864a79f

Browse files
authored
feat: reduce hot paths memory usage (#56)
* fix: no pretty print output * fix: typo * feat: cache file read by stat mod time
1 parent 92e33e5 commit 864a79f

File tree

4 files changed

+83
-8
lines changed

4 files changed

+83
-8
lines changed

fs.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"sync"
6+
)
7+
8+
func readFileWithStatCache(file string) func() ([]byte, error) {
9+
mu := new(sync.RWMutex)
10+
var (
11+
lastReadContent []byte
12+
lastStat os.FileInfo
13+
)
14+
15+
fast := func() (bool, []byte, error) {
16+
stat, err := os.Stat(file)
17+
if err != nil {
18+
return false, nil, err
19+
}
20+
21+
mu.RLock()
22+
defer mu.RUnlock()
23+
24+
if lastStat == nil {
25+
// no cache
26+
return false, nil, nil
27+
}
28+
29+
if lastStat.ModTime() == stat.ModTime() {
30+
return true, lastReadContent, nil
31+
}
32+
33+
// mod time changed
34+
return false, nil, nil
35+
}
36+
37+
slow := func() ([]byte, error) {
38+
mu.Lock()
39+
defer mu.Unlock()
40+
41+
stat, err := os.Stat(file)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
if lastStat != nil && lastStat.ModTime() == stat.ModTime() {
47+
return lastReadContent, nil
48+
}
49+
50+
lastStat = stat
51+
lastReadContent = nil
52+
53+
content, err := os.ReadFile(file)
54+
if err != nil {
55+
return nil, err
56+
}
57+
lastReadContent = content
58+
59+
return content, nil
60+
}
61+
62+
return func() ([]byte, error) {
63+
readFromCache, content, err := fast()
64+
if err != nil {
65+
return nil, err
66+
}
67+
if readFromCache {
68+
return content, nil
69+
}
70+
71+
return slow()
72+
}
73+
}

server.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,9 @@ func (server *dbServer) responseData(w http.ResponseWriter, data interface{}, st
152152
w.WriteHeader(statusCode)
153153

154154
enc := json.NewEncoder(w)
155-
enc.SetIndent("", " ")
156155
if encodeErr := enc.Encode(data); encodeErr != nil {
157156
server.logger.Error(encodeErr, "failed to write response")
158-
w.WriteHeader(http.StatusCreated)
157+
w.WriteHeader(http.StatusInternalServerError)
159158
return
160159
}
161160
}

server_auth.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"fmt"
55
"net/http"
6-
"os"
76
"strings"
87

98
"github.com/golang-jwt/jwt"
@@ -66,14 +65,16 @@ func (opts *ServerAuthOptions) createAuthMiddleware(
6665

6766
switch {
6867
case opts.RSAPublicKeyFilePath != "":
68+
keyReader := readFileWithStatCache(opts.RSAPublicKeyFilePath)
69+
6970
jwtParser.ValidMethods = append(
7071
jwtParser.ValidMethods,
7172
jwt.SigningMethodRS256.Name,
7273
jwt.SigningMethodRS384.Name,
7374
jwt.SigningMethodRS512.Name,
7475
)
7576
jwtKeyFunc = func(t *jwt.Token) (interface{}, error) {
76-
b, err := os.ReadFile(opts.RSAPublicKeyFilePath)
77+
b, err := keyReader()
7778
if err != nil {
7879
return nil, err
7980
}
@@ -85,14 +86,16 @@ func (opts *ServerAuthOptions) createAuthMiddleware(
8586
return v, nil
8687
}
8788
case opts.TokenFilePath != "":
89+
tokenReader := readFileWithStatCache(opts.TokenFilePath)
90+
8891
jwtParser.ValidMethods = append(
8992
jwtParser.ValidMethods,
9093
jwt.SigningMethodHS256.Name,
9194
jwt.SigningMethodHS384.Name,
9295
jwt.SigningMethodHS512.Name,
9396
)
9497
jwtKeyFunc = func(t *jwt.Token) (interface{}, error) {
95-
b, err := os.ReadFile(opts.TokenFilePath)
98+
b, err := tokenReader()
9699
if err != nil {
97100
return nil, err
98101
}

server_security.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ func (opts *ServerSecurityOptions) defaults() error {
3030
func (opts *ServerSecurityOptions) createTableOrViewAccessCheckMiddleware(
3131
responseErr func(w http.ResponseWriter, err error),
3232
) func(http.Handler) http.Handler {
33-
accesibleTableOrViews := make(map[string]struct{})
33+
accessibleTableOrViews := make(map[string]struct{})
3434
for _, t := range opts.EnabledTableOrViews {
35-
accesibleTableOrViews[t] = struct{}{}
35+
accessibleTableOrViews[t] = struct{}{}
3636
}
3737

3838
return func(next http.Handler) http.Handler {
3939
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
4040
target := chi.URLParam(req, routeVarTableOrView)
4141

42-
if _, ok := accesibleTableOrViews[target]; !ok {
42+
if _, ok := accessibleTableOrViews[target]; !ok {
4343
responseErr(w, ErrAccessRestricted)
4444
return
4545
}

0 commit comments

Comments
 (0)