From 3a97cf68cd00708de4f2c932399b3eb2941e9b6a Mon Sep 17 00:00:00 2001 From: Daniel Martins Date: Wed, 19 Aug 2026 16:39:39 +0300 Subject: [PATCH 1/3] fix: Record GetFilters API duration, upgrade GAF to v0.16.1 --- bundle/bundle_manager.go | 2 +- go.mod | 20 +++++----- go.sum | 44 ++++++++++----------- internal/uploadrevision/upload_revision.go | 2 +- internal/util/supportedfiles/filter.go | 27 ++++++++++++- internal/util/supportedfiles/filter_test.go | 7 ++-- 6 files changed, 64 insertions(+), 38 deletions(-) diff --git a/bundle/bundle_manager.go b/bundle/bundle_manager.go index e274709..9b7e555 100644 --- a/bundle/bundle_manager.go +++ b/bundle/bundle_manager.go @@ -79,7 +79,7 @@ func NewBundleManager( errorReporter: errorReporter, logger: logger, trackerFactory: trackerFactory, - supportedFilesFilter: supportedfiles.NewSupportedFilesFilter(deepcodeClient, logger), + supportedFilesFilter: supportedfiles.NewSupportedFilesFilter(deepcodeClient, logger, analyticsClient), analytics: analyticsClient, } } diff --git a/go.mod b/go.mod index efabb7a..7a59be8 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,11 @@ require ( github.com/pkg/errors v0.9.1 github.com/puzpuzpuz/xsync v1.5.2 github.com/rs/zerolog v1.34.0 - github.com/snyk/error-catalog-golang-public v0.0.0-20260205094614-116c03822905 - github.com/snyk/go-application-framework v0.11.0 + github.com/snyk/error-catalog-golang-public v0.0.0-20260806122555-28dc45bbbde6 + github.com/snyk/go-application-framework v0.16.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.11.1 - golang.org/x/net v0.55.0 + golang.org/x/net v0.57.0 ) require ( @@ -85,7 +85,7 @@ require ( github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect - github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530 // indirect + github.com/snyk/go-httpauth v0.0.0-20260810142636-0f6182aaccbc // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/speakeasy-api/jsonpath v0.6.1 // indirect github.com/speakeasy-api/openapi-overlay v0.10.1 // indirect @@ -100,13 +100,13 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.53.0 // indirect - golang.org/x/mod v0.36.0 // indirect + golang.org/x/crypto v0.54.0 // indirect + golang.org/x/mod v0.37.0 // indirect golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.21.0 // indirect - golang.org/x/sys v0.46.0 // indirect - golang.org/x/text v0.38.0 // indirect - golang.org/x/tools v0.45.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250404141209-ee84b53bf3d0 // indirect google.golang.org/grpc v1.71.0 // indirect google.golang.org/protobuf v1.36.6 // indirect diff --git a/go.sum b/go.sum index 9655aad..aae48e5 100644 --- a/go.sum +++ b/go.sum @@ -244,12 +244,12 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/snyk/error-catalog-golang-public v0.0.0-20260205094614-116c03822905 h1:pUe6iOWHEOFY0t4u4ssXeTqpMmZBu1xq06VBFI9zUik= -github.com/snyk/error-catalog-golang-public v0.0.0-20260205094614-116c03822905/go.mod h1:Ytttq7Pw4vOCu9NtRQaOeDU2dhBYUyNBe6kX4+nIIQ4= -github.com/snyk/go-application-framework v0.11.0 h1:lOZhYO8JmzMoZKQbCb/jHMnrB/N3TvX8Z6oE/L9OQ7o= -github.com/snyk/go-application-framework v0.11.0/go.mod h1:9GV/CTAhM8PT9MbxwYt/Za7tKDtw/Wuq6SyCu1XFzvk= -github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530 h1:s9PHNkL6ueYRiAKNfd8OVxlUOqU3qY0VDbgCD1f6WQY= -github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530/go.mod h1:88KbbvGYlmLgee4OcQ19yr0bNpXpOr2kciOthaSzCAg= +github.com/snyk/error-catalog-golang-public v0.0.0-20260806122555-28dc45bbbde6 h1:XUPFP85nBh+zDCTvxxBuouZP9yG7H1qXZiMGFVMWVKM= +github.com/snyk/error-catalog-golang-public v0.0.0-20260806122555-28dc45bbbde6/go.mod h1:0dz+HUR/r7VLlQpLfF0a/F1tdHH84NLTZzCxjZ+Q1nk= +github.com/snyk/go-application-framework v0.16.1 h1:k4eyP4EX/kqnNyu5uuFLEw4wflTom22ea23ZuG74JU8= +github.com/snyk/go-application-framework v0.16.1/go.mod h1:qJBU+FIY8s/lIg0IaKBj7WGeGERiWqyJvhajzxiA3Ls= +github.com/snyk/go-httpauth v0.0.0-20260810142636-0f6182aaccbc h1:tuZVhmJFxS4qJlwYIIIw8xgw3VaVqIR3IAV0WaaFVnI= +github.com/snyk/go-httpauth v0.0.0-20260810142636-0f6182aaccbc/go.mod h1:f42qLL7WXOS0od7dXJV/hK3myjms/r6HsXgLrg1HRRY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/speakeasy-api/jsonpath v0.6.1 h1:FWbuCEPGaJTVB60NZg2orcYHGZlelbNJAcIk/JGnZvo= @@ -327,14 +327,14 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -346,16 +346,16 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= -golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -381,24 +381,24 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= -golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/uploadrevision/upload_revision.go b/internal/uploadrevision/upload_revision.go index cb19fa6..f032ed1 100644 --- a/internal/uploadrevision/upload_revision.go +++ b/internal/uploadrevision/upload_revision.go @@ -60,7 +60,7 @@ func NewUploadRevision(httpClient *http.Client, cfg fileupload.Config, deepcodeC ) return &uploadRevision{ client: client, - supportedFilesFilter: supportedfiles.NewSupportedFilesFilter(deepcodeClient, logger), + supportedFilesFilter: supportedfiles.NewSupportedFilesFilter(deepcodeClient, logger, analyticsClient), logger: logger, analytics: analyticsClient, trackerFactory: trackerFactory, diff --git a/internal/util/supportedfiles/filter.go b/internal/util/supportedfiles/filter.go index c9c5f73..e394fdf 100644 --- a/internal/util/supportedfiles/filter.go +++ b/internal/util/supportedfiles/filter.go @@ -19,6 +19,8 @@ import ( "context" "os" "path/filepath" + "sync/atomic" + "time" "github.com/puzpuzpuz/xsync" "github.com/rs/zerolog" @@ -29,25 +31,48 @@ import ( const maxFileSize = 1024 * 1024 +const ( + // GAF file-filter durationMs - get_filters_ms = filtering + non-network work + metricGetFiltersMs = "get_filters_ms" + metricGetFiltersCalls = "get_filters_calls" +) + type SupportedFilesFilter struct { client deepcode.DeepcodeClient logger *zerolog.Logger + analytics analytics.Analytics supportedExtensions *xsync.MapOf[string, bool] supportedConfigFiles *xsync.MapOf[string, bool] + apiDurationMs atomic.Int64 + apiCallCount atomic.Int64 } -func NewSupportedFilesFilter(client deepcode.DeepcodeClient, logger *zerolog.Logger) *SupportedFilesFilter { +func NewSupportedFilesFilter(client deepcode.DeepcodeClient, logger *zerolog.Logger, analyticsClient analytics.Analytics) *SupportedFilesFilter { return &SupportedFilesFilter{ client: client, logger: logger, + analytics: analyticsClient, supportedExtensions: xsync.NewMapOf[bool](), supportedConfigFiles: xsync.NewMapOf[bool](), } } +func (s *SupportedFilesFilter) recordFiltersAPICall(elapsed time.Duration) { + totalMs := s.apiDurationMs.Add(elapsed.Milliseconds()) + calls := s.apiCallCount.Add(1) + + if s.analytics == nil { + return + } + s.analytics.AddExtensionIntegerValue(metricGetFiltersMs, int(totalMs)) + s.analytics.AddExtensionIntegerValue(metricGetFiltersCalls, int(calls)) +} + func (s *SupportedFilesFilter) isPathSupported(ctx context.Context, path string) (bool, error) { if s.supportedExtensions.Size() == 0 && s.supportedConfigFiles.Size() == 0 { + start := time.Now() filters, err := s.client.GetFilters(ctx) + s.recordFiltersAPICall(time.Since(start)) if err != nil { s.logger.Error().Err(err).Msg("could not get filters") return false, err diff --git a/internal/util/supportedfiles/filter_test.go b/internal/util/supportedfiles/filter_test.go index 9dbb2fd..80a2c7c 100644 --- a/internal/util/supportedfiles/filter_test.go +++ b/internal/util/supportedfiles/filter_test.go @@ -24,6 +24,7 @@ import ( "github.com/golang/mock/gomock" "github.com/rs/zerolog" + "github.com/snyk/go-application-framework/pkg/analytics" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -40,7 +41,7 @@ func Test_IsFileSupported_Extensions(t *testing.T) { Extensions: []string{".java"}, }, nil) - filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t)) + filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t), analytics.New()) dir := t.TempDir() t.Run("should return true for supported languages", func(t *testing.T) { @@ -77,7 +78,7 @@ func Test_IsFileSupported_ConfigFiles(t *testing.T) { }, nil }) - filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t)) + filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t), analytics.New()) dir := t.TempDir() t.Run("should return true for supported config files", func(t *testing.T) { @@ -106,7 +107,7 @@ func Test_IsFileSupported_FileSize(t *testing.T) { Extensions: []string{".java"}, }, nil) - filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t)) + filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t), analytics.New()) dir := t.TempDir() t.Run("should return false for empty files", func(t *testing.T) { From ffaac534a2eb0e44db5a5800be0013bb04df6d5b Mon Sep 17 00:00:00 2001 From: Daniel Martins Date: Wed, 19 Aug 2026 16:50:52 +0300 Subject: [PATCH 2/3] fix: Guard GetFilters metric accumulation and publish with a mutex --- internal/util/supportedfiles/filter.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/internal/util/supportedfiles/filter.go b/internal/util/supportedfiles/filter.go index e394fdf..bd44e6d 100644 --- a/internal/util/supportedfiles/filter.go +++ b/internal/util/supportedfiles/filter.go @@ -19,7 +19,7 @@ import ( "context" "os" "path/filepath" - "sync/atomic" + "sync" "time" "github.com/puzpuzpuz/xsync" @@ -43,8 +43,9 @@ type SupportedFilesFilter struct { analytics analytics.Analytics supportedExtensions *xsync.MapOf[string, bool] supportedConfigFiles *xsync.MapOf[string, bool] - apiDurationMs atomic.Int64 - apiCallCount atomic.Int64 + apiMu sync.Mutex + apiDurationMs int64 + apiCallCount int64 } func NewSupportedFilesFilter(client deepcode.DeepcodeClient, logger *zerolog.Logger, analyticsClient analytics.Analytics) *SupportedFilesFilter { @@ -58,14 +59,17 @@ func NewSupportedFilesFilter(client deepcode.DeepcodeClient, logger *zerolog.Log } func (s *SupportedFilesFilter) recordFiltersAPICall(elapsed time.Duration) { - totalMs := s.apiDurationMs.Add(elapsed.Milliseconds()) - calls := s.apiCallCount.Add(1) + s.apiMu.Lock() + defer s.apiMu.Unlock() + + s.apiDurationMs += elapsed.Milliseconds() + s.apiCallCount++ if s.analytics == nil { return } - s.analytics.AddExtensionIntegerValue(metricGetFiltersMs, int(totalMs)) - s.analytics.AddExtensionIntegerValue(metricGetFiltersCalls, int(calls)) + s.analytics.AddExtensionIntegerValue(metricGetFiltersMs, int(s.apiDurationMs)) + s.analytics.AddExtensionIntegerValue(metricGetFiltersCalls, int(s.apiCallCount)) } func (s *SupportedFilesFilter) isPathSupported(ctx context.Context, path string) (bool, error) { From 22797c990aba4df3224612a306abf24ddf10facc Mon Sep 17 00:00:00 2001 From: Daniel Martins Date: Wed, 19 Aug 2026 18:19:56 +0300 Subject: [PATCH 3/3] test: Assert GetFilters analytics values and single-fetch caching --- internal/util/supportedfiles/filter_test.go | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/internal/util/supportedfiles/filter_test.go b/internal/util/supportedfiles/filter_test.go index 80a2c7c..18743db 100644 --- a/internal/util/supportedfiles/filter_test.go +++ b/internal/util/supportedfiles/filter_test.go @@ -125,6 +125,34 @@ func Test_IsFileSupported_FileSize(t *testing.T) { }) } +func Test_IsFileSupported_Analytics(t *testing.T) { + ctrl := gomock.NewController(t) + mockSnykCodeClient := deepcodeMocks.NewMockDeepcodeClient(ctrl) + mockSnykCodeClient.EXPECT().GetFilters(gomock.Any()).Return(deepcode.FiltersResponse{ + ConfigFiles: []string{}, + Extensions: []string{".java"}, + }, nil) + + recorder := newRecordingAnalytics() + filter := supportedfiles.NewSupportedFilesFilter(mockSnykCodeClient, newLogger(t), recorder) + dir := t.TempDir() + + for _, name := range []string{"A.java", "B.java", "C.java"} { + supported, err := filter.IsFileSupported(t.Context(), createFile(t, dir, name)) + require.NoError(t, err) + require.True(t, supported) + } + + t.Run("should report the GetFilters API duration", func(t *testing.T) { + // The duration itself is not asserted: the client is mocked, so it rounds to 0ms. + assert.Contains(t, recorder.extensionValues, "get_filters_ms") + }) + + t.Run("should fetch the filters once for the whole scan", func(t *testing.T) { + assert.Equal(t, 1, recorder.extensionValues["get_filters_calls"]) + }) +} + func createFile(t *testing.T, dir, name string) string { t.Helper() path := filepath.Join(dir, name) @@ -137,3 +165,22 @@ func newLogger(t *testing.T) *zerolog.Logger { logger := zerolog.New(zerolog.NewTestWriter(t)) return &logger } + +// recordingAnalytics captures the reported extension values. GAF provides no mock for +// analytics.Analytics, and its collector offers no way to read recorded values back, so the +// real implementation is embedded and the one method under test is shadowed. +type recordingAnalytics struct { + analytics.Analytics + extensionValues map[string]int +} + +func newRecordingAnalytics() *recordingAnalytics { + return &recordingAnalytics{ + Analytics: analytics.New(), + extensionValues: map[string]int{}, + } +} + +func (r *recordingAnalytics) AddExtensionIntegerValue(key string, value int) { + r.extensionValues[key] = value +}