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

all: GraphQL API Versioning #3185

Merged
merged 1 commit into from
Aug 23, 2022
Merged

all: GraphQL API Versioning #3185

merged 1 commit into from
Aug 23, 2022

Conversation

kamilkisiela
Copy link
Contributor

@kamilkisiela kamilkisiela commented Jan 25, 2022

This is the initial implementation of versioning.

  • remove the example implementation of a feature flag

I left few comments on the interesting parts of the code to make it easier to review.

Continues #3155

#[derive(Clone, PartialEq)]
pub enum FeatureFlag {
// A description of the feature. Give it a little context.
BasicOrdering,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

An example flag, just to demo the versioning.

Comment on lines 88 to 90
if version.supports(FeatureFlag::BasicOrdering) {
add_order_direction_enum(&mut schema);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

An example implementation of a feature flag.

@dotansimha dotansimha changed the title GraphQL API Versioning feat(core,graph,graphql,store,server,node): GraphQL API Versioning May 3, 2022
@dotansimha dotansimha linked an issue May 3, 2022 that may be closed by this pull request
@evaporei
Copy link
Contributor

@kamilkisiela is this ready to review? I see that the CI is failing.

@kamilkisiela
Copy link
Contributor Author

@evaporei yes, the code won't change much even after a rebase. I've got some questions both in code and gh comments, because I'm not sure if it's the right implementation.

graph/src/data/query/query.rs Outdated Show resolved Hide resolved
graph/src/data/query/query.rs Outdated Show resolved Hide resolved
@@ -154,10 +154,11 @@ where
// while the query is running. `self.store` can not be used after this
// point, and everything needs to go through the `store` we are
// setting up here
let store = self.store.query_store(target, false).await?;

let store = self.store.query_store(target.clone(), false).await?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the query store should 'bind' to the version in the same way it binds to the deployment hash, so that api_schema() would always return the schema for that version. That makes it less likely that versions get switched during query execution. And the QueryStore is only used for the duration of executing one query.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

server/index-node/src/schema.graphql Outdated Show resolved Hide resolved
server/websocket/src/connection.rs Outdated Show resolved Hide resolved
store/postgres/src/subgraph_store.rs Outdated Show resolved Hide resolved
@@ -87,7 +87,7 @@ pub struct StoreInner {
conn_round_robin_counter: AtomicUsize,

/// A cache of commonly needed data about a subgraph.
subgraph_cache: Mutex<LruCache<DeploymentHash, SubgraphInfo>>,
subgraph_cache: Mutex<LruCache<(DeploymentHash, VersionNumber), SubgraphInfo>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about keeping DeploymentHash as the cache key and changing SubgraphInfo.api to be a HashMap<Version, Arc<ApiSchema>>? As long as there's only a few API versions, we could even populate that HashMap eagerly; once there are more versions, we might want to populate entries there on demand.

That would make it possible to not pass in a version to subgraph_info and only use it when we actually need an ApiSchema in the QueryStore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@azf20
Copy link
Contributor

azf20 commented Jun 14, 2022

hey @kamilkisiela have you got further updates here? just checking that you are not blocked

@kamilkisiela
Copy link
Contributor Author

@azf20 no updates, I'm trying to implement support for interfaces in #3184 and once it's ready, I'll apply David's comments here.

@kamilkisiela
Copy link
Contributor Author

@lutter ready for a review again, I implemented everything you asked for.

Those two failing tests (just the order of fields) are related to the fact I moved the ordering logic behind a feature flag to show what the versioning will look like. Once I remove the example feature flag, it will be fine.

@kamilkisiela
Copy link
Contributor Author

@azf20 I read the GIP and applied some changes to the PR. When we get a version range, the semver package resolves to the most recent applicable version.

Copy link
Collaborator

@lutter lutter left a comment

Choose a reason for hiding this comment

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

The main thing we need to clear up is what the initial version(s) should be

let mut map = HashMap::new();

// Sort version by major, minor, patch, from higher to lower.
supported_versions.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this sort necessary? They all go into a HashMap afterwards, so doesn't seem like it's needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did a refactor, now only the vector is sorted

let keys: Vec<Version> = VERSIONS.clone().into_keys().collect();

// Versions are sorted by major, minor, patch, from higher to lower.
keys.first().unwrap().to_string()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is wrong HashMap.into_keys will return keys in arbitrary order. BTreeMap would work, but I think first would return the smallest version, so either reverse the iterator or put the keys into a Vec and sort that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did a refactor and it's now using VERSION_COLLECTION: HashMap<Version, Vec<FeatureFlag>> to get the feature flags and VERSIONS: Vec<&'static Version> (sorted from latest to oldest version, higher -> lower).

The VERSIONS vector is sorted because of the logic in resolve() function.


impl ApiVersion {
pub fn new(version_requirement: &VersionReq) -> Result<Self, String> {
let version = Self::resolve(&version_requirement);
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be cleaner if resolve returned Result<Self, String> instead of an Option

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does now, I did a refactor

),
];

if version.supports(FeatureFlag::BasicOrdering) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am losing track of the plot a little here - should we really have a version without ordering?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No no, it's just an example of how to use the feature flags
#3185 (comment)

@kamilkisiela kamilkisiela changed the title feat(core,graph,graphql,store,server,node): GraphQL API Versioning all: GraphQL API Versioning Jul 27, 2022
@kamilkisiela
Copy link
Contributor Author

@lutter I did a refactor, it includes your suggestions.

Copy link
Collaborator

@lutter lutter left a comment

Choose a reason for hiding this comment

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

Looks good now!

@azf20 Are you ok with the base version being 1.0.0? All fine by me, just wanted to check that that's where we should start.

@azf20
Copy link
Contributor

azf20 commented Jul 27, 2022

Thanks @kamilkisiela and @lutter! I think starting with 1.0.0 is ok, but also interested in @That3Percent's expectations of a 1.x release

@That3Percent
Copy link
Contributor

I think starting with 1.0.0 is ok, but also interested in @That3Percent's expectations of a 1.x release

I also think a 1.0.0 release is a good choice. Even if there are changes we know we would like to make, that's always the case so it's better to have a 1.0 and 2.0 than 0.x in perpetuity.

Copy link
Contributor

@That3Percent That3Percent left a comment

Choose a reason for hiding this comment

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

Currently the protocol expects version "0" to exist so that we can be backward-compatible with the existing attestation format. As implemented, graph-node is only supporting 1.0.0 here.

I think this is probably fine, as long as we default to 1.0 upstream from here (indexer agent, gateway) and accept that version "0" should never be queried directly (it would be a historical relic). Thoughts?

@azf20
Copy link
Contributor

azf20 commented Aug 12, 2022

Agree that this approach seems fine, and I think the GIP already allows for that kind of minimum version specification

@azf20
Copy link
Contributor

azf20 commented Aug 22, 2022

Hey @kamilkisiela I think this is good to merge, though looks like there are a couple of conflicts first! cc @lutter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add GraphQL versioning to graph-node
5 participants