-
Notifications
You must be signed in to change notification settings - Fork 981
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
Conversation
#[derive(Clone, PartialEq)] | ||
pub enum FeatureFlag { | ||
// A description of the feature. Give it a little context. | ||
BasicOrdering, |
There was a problem hiding this comment.
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.
graphql/src/schema/api.rs
Outdated
if version.supports(FeatureFlag::BasicOrdering) { | ||
add_order_direction_enum(&mut schema); | ||
} |
There was a problem hiding this comment.
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.
a26144e
to
4e1eaff
Compare
4e1eaff
to
1063f62
Compare
a77fde0
to
7699ddf
Compare
7699ddf
to
7caf53c
Compare
@kamilkisiela is this ready to review? I see that the CI is failing. |
@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. |
7caf53c
to
aeb6791
Compare
@@ -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?; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -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>>, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @kamilkisiela have you got further updates here? just checking that you are not blocked |
aeb6791
to
e8c3888
Compare
@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. |
@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. |
There was a problem hiding this 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()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
graphql/src/schema/api.rs
Outdated
), | ||
]; | ||
|
||
if version.supports(FeatureFlag::BasicOrdering) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
155907b
to
f9ad24f
Compare
@lutter I did a refactor, it includes your suggestions. |
f9ad24f
to
b00ddd6
Compare
There was a problem hiding this 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.
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 |
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. |
There was a problem hiding this 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?
Agree that this approach seems fine, and I think the GIP already allows for that kind of minimum version specification |
b00ddd6
to
2ca9c14
Compare
Hey @kamilkisiela I think this is good to merge, though looks like there are a couple of conflicts first! cc @lutter |
2ca9c14
to
1b0d091
Compare
This is the initial implementation of versioning.
I left few comments on the interesting parts of the code to make it easier to review.
Continues #3155