Skip to content

Commit

Permalink
caching for sourcemaps
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadman97 committed Apr 26, 2024
1 parent 411d0aa commit a09a833
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 41 deletions.
9 changes: 8 additions & 1 deletion backend/redis/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

type Config struct {
BypassCache bool
StoreNil bool
}

type Option func(cfg *Config)
Expand All @@ -20,6 +21,12 @@ func WithBypassCache(bypass bool) Option {
}
}

func WithStoreNil(storeNil bool) Option {
return func(cfg *Config) {
cfg.StoreNil = storeNil
}
}

// CachedEval will return the value at cacheKey if it exists.
// If it does not exist or is nil, CachedEval calls `fn()` to evaluate the result, and stores it at the cache key.
func CachedEval[T any](ctx context.Context, redis *Client, cacheKey string, lockTimeout, cacheExpiration time.Duration, fn func() (*T, error), opts ...Option) (value *T, err error) {
Expand All @@ -43,7 +50,7 @@ func CachedEval[T any](ctx context.Context, redis *Client, cacheKey string, lock
}
}()
}
if value, err = fn(); value == nil || err != nil {
if value, err = fn(); (!cfg.StoreNil && value == nil) || err != nil {
return
}
if err = redis.Cache.Set(&cache.Item{
Expand Down
79 changes: 45 additions & 34 deletions backend/stacktraces/enhancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (
"crypto/tls"
"encoding/base64"
"fmt"
"github.com/highlight-run/highlight/backend/redis"
"io"
"net/http"
"net/url"
"os"
"path"
"regexp"
"strings"
"time"

"github.com/andybalholm/brotli"
"github.com/go-sourcemap/sourcemap"
Expand Down Expand Up @@ -43,9 +45,9 @@ func init() {
customTransport := http.DefaultTransport.(*http.Transport).Clone()
customTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
client := &http.Client{Transport: customTransport}
fetch = NetworkFetcher{client: client}
fetch = NetworkFetcher{client: client, redis: redis.NewClient()}
} else {
fetch = NetworkFetcher{}
fetch = NetworkFetcher{redis: redis.NewClient()}
}
}

Expand All @@ -66,50 +68,59 @@ func (n DiskFetcher) fetchFile(ctx context.Context, href string) ([]byte, error)

type NetworkFetcher struct {
client *http.Client
redis *redis.Client
}

func (n NetworkFetcher) fetchFile(ctx context.Context, href string) ([]byte, error) {
span, ctx := util.StartSpanFromContext(ctx, "network.fetchFile")
defer span.Finish()

// check if source is a URL
_, err := url.ParseRequestURI(href)
if err != nil {
return nil, err
}
// get minified file
if n.client == nil {
n.client = http.DefaultClient
}
res, err := n.client.Get(href)
if err != nil {
return nil, e.Wrap(err, "error getting source file")
}
defer func(Body io.ReadCloser) {
err := Body.Close()
b, err := redis.CachedEval(ctx, n.redis, href, time.Second, time.Minute, func() (*[]byte, error) {
// check if source is a URL
_, err := url.ParseRequestURI(href)
if err != nil {
log.WithContext(ctx).Errorf("failed to close network reader %+v", err)
return nil, err
}
// get minified file
if n.client == nil {
n.client = http.DefaultClient
}
res, err := n.client.Get(href)
if err != nil {
return nil, e.Wrap(err, "error getting source file")
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
log.WithContext(ctx).Errorf("failed to close network reader %+v", err)
}
}(res.Body)
if res.StatusCode != http.StatusOK {
return nil, e.New("status code not OK")
}
}(res.Body)
if res.StatusCode != http.StatusOK {
return nil, e.New("status code not OK")
}

if res.Header.Get("Content-Encoding") == "br" {
out := &bytes.Buffer{}
if _, err := io.Copy(out, brotli.NewReader(res.Body)); err != nil {
return nil, e.New("failed to read brotli content")
if res.Header.Get("Content-Encoding") == "br" {
out := &bytes.Buffer{}
if _, err := io.Copy(out, brotli.NewReader(res.Body)); err != nil {
return nil, e.New("failed to read brotli content")
}
bt := out.Bytes()
return &bt, nil
}
return out.Bytes(), nil
}

// unpack file into slice
bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, e.Wrap(err, "error reading response body")
}
// unpack file into slice
bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return nil, e.Wrap(err, "error reading response body")
}

return &bodyBytes, nil
}, redis.WithStoreNil(true))

return bodyBytes, nil
if b == nil || err != nil {
return nil, err
}
return *b, nil
}

func limitMaxSize(value *string) *string {
Expand Down
13 changes: 7 additions & 6 deletions backend/stacktraces/enhancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/highlight-run/highlight/backend/redis"
"github.com/openlyinc/pointy"
"github.com/stretchr/testify/assert"
"testing"
Expand Down Expand Up @@ -107,7 +108,7 @@ func TestEnhanceStackTrace(t *testing.T) {
FunctionName: ptr.String("arrayIncludesWith"),
},
},
fetcher: NetworkFetcher{},
fetcher: NetworkFetcher{redis: redis.NewClient()},
err: e.New(""),
},
"test source mapping invalid trace:no related source map": {
Expand Down Expand Up @@ -157,7 +158,7 @@ func TestEnhanceStackTrace(t *testing.T) {
},
},
},
fetcher: NetworkFetcher{},
fetcher: NetworkFetcher{redis: redis.NewClient()},
err: e.New(""),
},
"test source mapping invalid trace:filename is not a url": {
Expand All @@ -182,7 +183,7 @@ func TestEnhanceStackTrace(t *testing.T) {
},
},
},
fetcher: NetworkFetcher{},
fetcher: NetworkFetcher{redis: redis.NewClient()},
err: e.New(""),
},
"test source mapping invalid trace:trace is nil": {
Expand Down Expand Up @@ -258,7 +259,7 @@ func TestEnhanceStackTrace(t *testing.T) {
FunctionName: ptr.String("Error"),
},
},
fetcher: NetworkFetcher{},
fetcher: NetworkFetcher{redis: redis.NewClient()},
err: e.New(""),
},
}
Expand Down Expand Up @@ -313,7 +314,7 @@ func TestEnhanceBackendNextServerlessTrace(t *testing.T) {
t.Fatalf("error creating storage client: %v", err)
}

fetch = NetworkFetcher{}
fetch = NetworkFetcher{redis: redis.NewClient()}

var stackFrameInput []*publicModelInput.StackFrameInput
err = json.Unmarshal([]byte(`[{"fileName":"/var/task/apps/magicsky/.next/server/app/(route-group-test)/[slug]/page-router-edge-test.js","lineNumber":1,"functionName":"s","columnNumber":3344,"error":"Error: 🎉 SSR Error with use-server: src/app-router/ssr/page.tsx"}]`), &stackFrameInput)
Expand Down Expand Up @@ -351,7 +352,7 @@ func TestEnhanceStackTraceProd(t *testing.T) {
t.Fatalf("error creating storage client: %v", err)
}

fetch = NetworkFetcher{}
fetch = NetworkFetcher{redis: redis.NewClient()}
mappedStackTrace, err := EnhanceStackTrace(ctx, []*publicModelInput.StackFrameInput{
{
FunctionName: nil,
Expand Down
33 changes: 33 additions & 0 deletions backend/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
hredis "github.com/highlight-run/highlight/backend/redis"
"io"
"net/http"
"os"
Expand Down Expand Up @@ -103,6 +104,7 @@ type Client interface {
type FilesystemClient struct {
origin string
fsRoot string
redis *hredis.Client
}

func (f *FilesystemClient) GetDirectDownloadURL(_ context.Context, projectId int, sessionId int, payloadType PayloadType, chunkId *int) (*string, error) {
Expand Down Expand Up @@ -305,6 +307,21 @@ func (f *FilesystemClient) ReadSourceMapFile(ctx context.Context, projectId int,
}
}

func (f *FilesystemClient) ReadSourceMapFileCached(ctx context.Context, projectId int, version *string, fileName string) ([]byte, error) {
span, ctx := util.StartSpanFromContext(ctx, "fs.ReadSourceMapFileCached")
defer span.Finish()
key := fmt.Sprintf("%s/%d/%s/%s", f.fsRoot, projectId, *version, fileName)
b, err := hredis.CachedEval(ctx, f.redis, key, time.Second, time.Minute, func() (*[]byte, error) {
bt, err := f.ReadSourceMapFile(ctx, projectId, version, fileName)
return &bt, err
}, hredis.WithStoreNil(true))

if b == nil || err != nil {
return nil, err
}
return *b, nil
}

func (f *FilesystemClient) GetAssetURL(_ context.Context, projectId string, hashVal string) (string, error) {
return fmt.Sprintf("%s/direct/assets/%s/%s", f.origin, projectId, hashVal), nil
}
Expand Down Expand Up @@ -456,6 +473,7 @@ type S3Client struct {
S3ClientEast2 *s3.Client
S3PresignClient *s3.PresignClient
URLSigner *sign.URLSigner
Redis *hredis.Client
}

func NewS3Client(ctx context.Context) (*S3Client, error) {
Expand Down Expand Up @@ -850,6 +868,21 @@ func (s *S3Client) ReadSourceMapFile(ctx context.Context, projectId int, version
return buf.Bytes(), nil
}

func (s *S3Client) ReadSourceMapFileCached(ctx context.Context, projectId int, version *string, fileName string) ([]byte, error) {
span, ctx := util.StartSpanFromContext(ctx, "s3.ReadSourceMapFileCached")
defer span.Finish()
key := s.sourceMapBucketKey(projectId, version, fileName)
b, err := hredis.CachedEval(ctx, s.Redis, *key, time.Second, time.Minute, func() (*[]byte, error) {
bt, err := s.ReadSourceMapFile(ctx, projectId, version, fileName)
return &bt, err
}, hredis.WithStoreNil(true))

if b == nil || err != nil {
return nil, err
}
return *b, nil
}

func (s *S3Client) GetDirectDownloadURL(_ context.Context, projectId int, sessionId int, payloadType PayloadType, chunkId *int) (*string, error) {
if s.URLSigner == nil {
return nil, nil
Expand Down

0 comments on commit a09a833

Please sign in to comment.