Skip to content

Commit c8d6722

Browse files
committed
fix: update handling cache
1 parent 601df99 commit c8d6722

File tree

5 files changed

+65
-22
lines changed

5 files changed

+65
-22
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ docker/garage/*
77
!docker/compose.yml
88
.moon/cache
99
.*.env
10-
httpie
10+
httpie
11+
.DS_Store

internal/cache/types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type CacheEntry struct {
1818
// Cache configuration
1919
const (
2020
DefaultCacheDuration = 5 * time.Minute
21-
MaxCacheSize = 100 * 1024 * 1024 // 100MB
21+
MaxCacheSize = 300 * 1024 * 1024 // 300MB
2222
CleanupInterval = 1 * time.Minute
23-
MinSizeForCompression = 1024 // Only compress files larger than 1KB
23+
MinSizeForCompression = 1 * 1024 * 1024 // Only compress files larger than 1MB
2424
)

internal/handlers/generic.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type Response struct {
2626
Headers http.Header
2727
Body interface{}
2828
ContentType string
29+
IsStreaming bool
2930
}
3031

3132
// HandlerFunc is the generic handler function type

internal/handlers/object.go

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,14 @@ func (h *ObjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
118118
func (h *ObjectHandler) handleGet(ctx context.Context, req *Request, input GetObjectRequest) (*Response, error) {
119119
bucket := req.PathParams["bucket"]
120120
key := req.PathParams["key"]
121-
122121
if bucket == "" || key == "" {
123122
return nil, &ValidationError{Field: "path", Message: "invalid bucket or key"}
124123
}
125124

126125
cacheKey := cache.GetCacheKey(bucket, key)
127126
acceptsGzip := strings.Contains(req.Headers.Get("Accept-Encoding"), "gzip")
128127

129-
// Check cache
128+
// Fast path: Check cache
130129
if entry, found := cache.GetFromCache(cacheKey); found {
131130
var responseData []byte
132131
var contentEncoding string
@@ -135,31 +134,29 @@ func (h *ObjectHandler) handleGet(ctx context.Context, req *Request, input GetOb
135134
contentEncoding = "gzip"
136135
} else {
137136
responseData = entry.Data
138-
contentEncoding = ""
139-
}
140-
141-
headers := http.Header{
142-
"Content-Type": []string{entry.ContentType},
143-
"Content-Length": []string{fmt.Sprintf("%d", len(responseData))},
144-
"Last-Modified": []string{entry.LastModified.UTC().Format(http.TimeFormat)},
145-
"ETag": []string{entry.ETag},
146-
"Content-Encoding": []string{contentEncoding},
147137
}
148138

149139
return &Response{
150-
StatusCode: http.StatusOK,
151-
Headers: headers,
140+
StatusCode: http.StatusOK,
141+
Headers: http.Header{
142+
"Content-Type": []string{entry.ContentType},
143+
"Content-Length": []string{fmt.Sprintf("%d", len(responseData))},
144+
"Last-Modified": []string{entry.LastModified.UTC().Format(http.TimeFormat)},
145+
"ETag": []string{entry.ETag},
146+
"Content-Encoding": []string{contentEncoding},
147+
"X-Cache": []string{"HIT"},
148+
},
152149
Body: responseData,
153150
ContentType: entry.ContentType,
154151
}, nil
155152
}
156153

157-
h.logger.Info("cache miss, fetching from storage")
158-
154+
// Get object stats first
159155
obj, err := h.client.GetObject(ctx, bucket, key, minio.GetObjectOptions{})
160156
if err != nil {
161157
return nil, err
162158
}
159+
defer obj.Close()
163160

164161
info, err := obj.Stat()
165162
if err != nil {
@@ -169,7 +166,40 @@ func (h *ObjectHandler) handleGet(ctx context.Context, req *Request, input GetOb
169166
return nil, err
170167
}
171168

172-
// Read the entire object into memory
169+
// For very large files, stream directly
170+
if info.Size > cache.MaxCacheSize*2 {
171+
h.logger.Info("large file detected, streaming response",
172+
"size", info.Size,
173+
"content_type", info.ContentType,
174+
)
175+
headers := http.Header{
176+
"Content-Type": []string{info.ContentType},
177+
"Last-Modified": []string{info.LastModified.UTC().Format(http.TimeFormat)},
178+
"ETag": []string{info.ETag},
179+
"X-Cache": []string{"BYPASS"},
180+
}
181+
182+
if acceptsGzip && cache.ShouldCompress(info.ContentType, info.Size) {
183+
return &Response{
184+
StatusCode: http.StatusOK,
185+
Headers: headers,
186+
Body: obj,
187+
ContentType: info.ContentType,
188+
IsStreaming: true,
189+
}, nil
190+
}
191+
192+
headers.Set("Content-Length", fmt.Sprintf("%d", info.Size))
193+
return &Response{
194+
StatusCode: http.StatusOK,
195+
Headers: headers,
196+
Body: obj,
197+
ContentType: info.ContentType,
198+
IsStreaming: true,
199+
}, nil
200+
}
201+
202+
// For smaller files, read into memory
173203
data, err := io.ReadAll(obj)
174204
if err != nil {
175205
return nil, fmt.Errorf("failed to read object data: %w", err)
@@ -180,13 +210,18 @@ func (h *ObjectHandler) handleGet(ctx context.Context, req *Request, input GetOb
180210
"content_type", info.ContentType,
181211
)
182212

183-
// Cache the object
184-
cache.AddToCache(cacheKey, data, info.ContentType, int64(len(data)), info.LastModified, info.ETag)
213+
// Cache smaller files in a goroutine
214+
if int64(len(data)) <= cache.MaxCacheSize/2 {
215+
go func() {
216+
cache.AddToCache(cacheKey, data, info.ContentType, int64(len(data)), info.LastModified, info.ETag)
217+
}()
218+
}
185219

186220
headers := http.Header{
187221
"Content-Type": []string{info.ContentType},
188222
"Last-Modified": []string{info.LastModified.UTC().Format(http.TimeFormat)},
189223
"ETag": []string{info.ETag},
224+
"X-Cache": []string{"MISS"},
190225
}
191226

192227
responseData := data
@@ -201,7 +236,6 @@ func (h *ObjectHandler) handleGet(ctx context.Context, req *Request, input GetOb
201236
}
202237
}
203238

204-
// Set Content-Length after compression decision
205239
headers.Set("Content-Length", fmt.Sprintf("%d", len(responseData)))
206240

207241
return &Response{

prometheus.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
global:
2+
scrape_interval: 15s # How often to scrape targets
3+
4+
scrape_configs:
5+
- job_name: 'estrois' # Job name for your app's metrics
6+
static_configs:
7+
- targets: ['estrois:8080'] # Use service name and port from Docker Compose

0 commit comments

Comments
 (0)