Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v2][storage] Add dependency store to v2 storage interface #6297

Merged
merged 21 commits into from
Dec 7, 2024

Conversation

mahadzaryab1
Copy link
Collaborator

@mahadzaryab1 mahadzaryab1 commented Dec 2, 2024

Which problem is this PR solving?

Description of the changes

  • This PR creates a new factory in the depstore package for the DependencyReader and exposes a CreateDependencyReader function in the factoryadapter
  • This PR also changes some interface return types to structs because some structs implement multiple interfaces.
  • The new factory was integrated into the query service and the callsites were changed to use the v2 factory instead of the v1 factory to initialize the dependency reader.

How was this change tested?

  • CI / Unit Tests

Checklist

@mahadzaryab1
Copy link
Collaborator Author

@yurishkuro Where should we add the CreateDependencyReader function to? The GetStorageFactoryV2 in the storage extension returns a tracestore.Factory so I see the following options:

  1. Add CreateDependencyReader to tracestore.Factory. I don't think think this is a good idea because dependencies and traces are different concepts.
  2. Create a storage_v2.Factory that composes a tracestore.Factory and depstore.Factory.
  3. Refactor GetStorageFactoryV2 to something like GetTraceStoreFactory and then expose a new function called GetDepStoreFactory. This would allow us to keep our interfaces small and only get the interface that we need.

Let me know what you think and if you have any other ideas.

@mahadzaryab1 mahadzaryab1 added the changelog:new-feature Change that should be called out as new feature in CHANGELOG label Dec 2, 2024
Copy link

codecov bot commented Dec 2, 2024

Codecov Report

Attention: Patch coverage is 89.28571% with 3 lines in your changes missing coverage. Please review.

Project coverage is 96.14%. Comparing base (723202b) to head (47d5f2c).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...md/jaeger/internal/extension/jaegerquery/server.go 62.50% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6297      +/-   ##
==========================================
+ Coverage   96.11%   96.14%   +0.02%     
==========================================
  Files         356      356              
  Lines       20476    20489      +13     
==========================================
+ Hits        19681    19699      +18     
+ Misses        607      603       -4     
+ Partials      188      187       -1     
Flag Coverage Δ
badger_v1 8.82% <0.00%> (-0.02%) ⬇️
badger_v2 1.61% <0.00%> (-0.01%) ⬇️
cassandra-4.x-v1-manual 14.71% <0.00%> (-0.03%) ⬇️
cassandra-4.x-v2-auto 1.56% <0.00%> (-0.01%) ⬇️
cassandra-4.x-v2-manual 1.56% <0.00%> (-0.01%) ⬇️
cassandra-5.x-v1-manual 14.71% <0.00%> (-0.03%) ⬇️
cassandra-5.x-v2-auto 1.56% <0.00%> (-0.01%) ⬇️
cassandra-5.x-v2-manual 1.56% <0.00%> (-0.01%) ⬇️
elasticsearch-6.x-v1 18.44% <0.00%> (-0.04%) ⬇️
elasticsearch-7.x-v1 18.52% <0.00%> (-0.02%) ⬇️
elasticsearch-8.x-v1 18.67% <0.00%> (-0.03%) ⬇️
elasticsearch-8.x-v2 1.61% <0.00%> (-0.01%) ⬇️
grpc_v1 10.28% <0.00%> (-0.02%) ⬇️
grpc_v2 7.83% <0.00%> (-0.03%) ⬇️
kafka-v1 8.51% <0.00%> (-0.02%) ⬇️
kafka-v2 1.61% <0.00%> (-0.01%) ⬇️
memory_v2 1.61% <0.00%> (-0.02%) ⬇️
opensearch-1.x-v1 18.57% <0.00%> (-0.03%) ⬇️
opensearch-2.x-v1 18.57% <0.00%> (-0.03%) ⬇️
opensearch-2.x-v2 1.61% <0.00%> (-0.01%) ⬇️
tailsampling-processor 0.45% <0.00%> (-0.01%) ⬇️
unittests 95.05% <89.28%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@yurishkuro
Copy link
Member

I like option 3. We always had a split brain on dependencies, it was defined in a different package but part of the same factory interface. +1 to move to a different interface.

@mahadzaryab1 mahadzaryab1 changed the title [WIP][v2][storage] Add dependency store to v2 storage interface [v2][storage] Add dependency store to v2 storage interface Dec 6, 2024
@mahadzaryab1 mahadzaryab1 marked this pull request as ready for review December 6, 2024 01:10
@mahadzaryab1 mahadzaryab1 requested a review from a team as a code owner December 6, 2024 01:10
@@ -74,7 +73,7 @@ func GetMetricStorageFactory(name string, host component.Host) (storage.MetricSt
return mf, nil
}

func GetStorageFactoryV2(name string, host component.Host) (tracestore.Factory, error) {
func GetStorageFactoryV2(name string, host component.Host) (*factoryadapter.Factory, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't know about this - adapter is implementation detail, this function should not leak it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yurishkuro I was trying to follow's Go's best practice here of returning structs instead of interfaces. If we want to keep the interface return, we'll need to define another function GetDependencyStoreFactory with a different return type (depstore.Reader) but it will do the exact same thing. Let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not the best practice in this case. The return-struct best practice applies to constructors. GetStorageFactoryV2 is not a constructor, it's an accessor that is supposed to return a storage interface. The caller of this function is not supposed to know about the implementation of that interface and accordingly should not be able to take dependency on the concrete returned type, which could be wider than the interface alone.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good - so should we have two accessors here (something like GetTraceStoreFactory and GetDependencyStoreFactory)? Their return types would be different but the implementations would be the same.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we should. Implementations are only the same today, not in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good to me! done

Comment on lines 83 to 86
df, err := jaegerstorage.GetDependencyStoreFactory(s.config.Storage.TracesPrimary, host)
if err != nil {
return fmt.Errorf("cannot find factory for dependency storage %s: %w", s.config.Storage.TracesPrimary, err)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yurishkuro is this okay? we're using the traces storage to initialize the dependency storage factory

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok for now

@@ -114,7 +114,7 @@ by default uses only in-memory database.`,
if err != nil {
logger.Fatal("Failed to create span writer", zap.Error(err))
}
dependencyReader, err := storageFactory.CreateDependencyReader()
dependencyReader, err := v2Factory.CreateDependencyReader()
Copy link
Collaborator Author

@mahadzaryab1 mahadzaryab1 Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yurishkuro for v1, the factory adapter is created directly so we have direct access to this function - is this okay?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@@ -511,10 +512,13 @@ func TestGetDependenciesSuccessGRPC(t *testing.T) {
withServerAndClient(t, func(server *grpcServer, client *grpcClient) {
expectedDependencies := []model.DependencyLink{{Parent: "killer", Child: "queen", CallCount: 12}}
endTs := time.Now().UTC()
expectedEndTs := endTs.Add(time.Duration(-1) * defaultDependencyLookbackDuration)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is expectedEndTs offset from endTs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be called startTs perhaps?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, does Add(-defaultDependencyLookbackDuration) work?

Copy link
Collaborator Author

@mahadzaryab1 mahadzaryab1 Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yurishkuro Its a bit weird because here startTime is passed into endTs (https://github.com/jaegertracing/jaeger/blob/main/cmd/query/app/grpc_handler.go#L226). Do you know why that is? I thought it would be passed the endTs and then the lookback

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like a bug

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened a patch at #6324

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yurishkuro should be good to go now

return getV2FactoryAdapter(name, host)
}

func GetDependencyStoreFactory(name string, host component.Host) (depstore.Factory, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been looking at missing test coverage and realized that it's hard to get error coverage for this code path. We have this Extension API:

type Extension interface {
	extension.Extension
	TraceStorageFactory(name string) (storage.Factory, bool)
	MetricStorageFactory(name string) (storage.MetricStoreFactory, bool)
}

Shouldn't it be exposing v2 factory instead of v1? And either it should have deps-related method OR there shouldn't be a static function for deps and instead it should be a runtime interface check, as we discussed on the ticket.

Maybe this is transitional? I actually don't like that the adapter is being applied by the static functions - I think the right way would be to have the extension itself expose v2 API (instead of v1 API - we could get to v1 via downgreading, if needed).

Signed-off-by: Mahad Zaryab <[email protected]>
@yurishkuro yurishkuro enabled auto-merge (squash) December 7, 2024 23:10
@yurishkuro yurishkuro disabled auto-merge December 7, 2024 23:12
@yurishkuro yurishkuro merged commit b264d2c into jaegertracing:main Dec 7, 2024
53 of 54 checks passed
@mahadzaryab1 mahadzaryab1 deleted the v2-depreader branch December 8, 2024 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/storage changelog:new-feature Change that should be called out as new feature in CHANGELOG v2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants