What
subgraph-deploy names a deploy after the consumer repo's commit, not after the
subgraph it deploys. lib/subgraph.sh:
# Derive a deterministic deploy version from contract address and git commit.
# Usage: subgraph_deploy_version <address> <commit>
subgraph_deploy_version() {
local address="$1"
local commit="$2"
echo "${address}-${commit}"
}
and the task body supplies that commit from whatever repo it happens to be run
in:
commit="$(${pkgs.git}/bin/git rev-parse --short HEAD)"
for network in $(subgraph_networks ./subgraph/networks.json); do
address=$(subgraph_network_address ./subgraph/networks.json "$network")
version=$(subgraph_deploy_version "$address" "$commit")
name_and_version="''${GOLDSKY_SUBGRAPH_NAME}-$network/$version"
if ${goldsky}/bin/goldsky --token ''${GOLDSKY_TOKEN} subgraph list "$name_and_version" 2>/dev/null | grep -q "$name_and_version"; then
echo "Subgraph $name_and_version already deployed, skipping."
(address, consumer commit) is the whole identity, and it is also the skip key.
So any two deploys that agree on those two facts are treated as the same deploy,
whatever the manifest, schema or mappings actually say — the second one is
skipped and Goldsky keeps serving the first.
That is sound while the subgraph source is IN the consumer repo, because then
the commit does pin the source. It stops being sound the moment it is not.
Where this bites
rainlanguage/rain.metadata.deploy#4, under the subgraph split
(rainlanguage/rain.metadata#149). The deploy repo holds subgraph/networks.json
and nothing else under subgraph/; the manifest, schema and mappings are
fetched from rain.metadata at a metadata-ref chosen at dispatch time and
merged in beside the table. The address comes from the fetched-and-merged tree,
the commit comes from the deploy repo, and NEITHER of them is a function of the
source that was fetched. Two dispatches from one deploy-repo commit against two
different rain.metadata revisions produce one version, and the second deploy
is skipped.
Note this is not avoidable by pinning the input: the default metadata-ref is
main, which is itself a moving ref, so the collision happens on back-to-back
default dispatches that straddle a merge into rain.metadata — which is the
normal way the workflow gets used, not an exotic one.
Why a consuming repo cannot fix this on its own
The consumer's only handle is nix develop --command subgraph-deploy. The
version is computed inside the task, from git rev-parse --short HEAD in the
working tree, with no argument and no environment override to reach it.
The three consumer-side workarounds all fail:
- Fold the source revision into
GOLDSKY_SUBGRAPH_NAME. The consumer does
set that variable, but it is the deployed subgraph's public identity — the
thing consumers query as metaboard-base. Varying it per source revision
publishes a new subgraph per revision instead of a new version of one.
- Move
HEAD. The consumer could commit the fetched source locally so
rev-parse changes. That yields a version naming a commit that exists in no
repository, and it is only deterministic if commit metadata is pinned too —
otherwise a legitimate re-dispatch of the identical thing gets a fresh version
and re-deploys, which is the idempotency the skip exists to provide.
- Reject non-default
metadata-ref. Does not close it, per the note above:
the default is a moving ref.
Options
- Take the version as an input. An argument to
subgraph-deploy, or an
env var (SUBGRAPH_DEPLOY_VERSION_SUFFIX) that the task folds in when set.
Smallest, and it gives the consumer the handle rather than picking the
trade-off for them. Unset keeps today's string exactly.
- Derive the version from what is actually deployed. Hash the assembled
subgraph/ tree (post-codegen, pre-build) instead of asking git. This is
the one that is correct by construction — it does not care which repo the
files came from — but it changes every existing version string org-wide, so
it is a bigger call than it looks.
- Keep the commit, add the source.
<address>-<commit>-<source-rev> where
a source rev is supplied and <address>-<commit> where it is not.
(1) and (3) are the same change seen from either end; (2) is the one that stops
this recurring for the next consumer that splits its subgraph.
Not claimed
No wrong data has been served. The failure mode is a deploy that does not
happen: Goldsky keeps serving the previously deployed subgraph and the run
reports success, because "already deployed, skipping" is a success. It is
visible in the consumer's run summary today only because
rain.metadata.deploy#4 spells the empty case out; nothing in rainix says it.
What
subgraph-deploynames a deploy after the consumer repo's commit, not after thesubgraph it deploys.
lib/subgraph.sh:and the task body supplies that commit from whatever repo it happens to be run
in:
(address, consumer commit)is the whole identity, and it is also the skip key.So any two deploys that agree on those two facts are treated as the same deploy,
whatever the manifest, schema or mappings actually say — the second one is
skipped and Goldsky keeps serving the first.
That is sound while the subgraph source is IN the consumer repo, because then
the commit does pin the source. It stops being sound the moment it is not.
Where this bites
rainlanguage/rain.metadata.deploy#4, under the subgraph split
(rainlanguage/rain.metadata#149). The deploy repo holds
subgraph/networks.jsonand nothing else under
subgraph/; the manifest, schema and mappings arefetched from
rain.metadataat ametadata-refchosen at dispatch time andmerged in beside the table. The address comes from the fetched-and-merged tree,
the commit comes from the deploy repo, and NEITHER of them is a function of the
source that was fetched. Two dispatches from one deploy-repo commit against two
different
rain.metadatarevisions produce one version, and the second deployis skipped.
Note this is not avoidable by pinning the input: the default
metadata-refismain, which is itself a moving ref, so the collision happens on back-to-backdefault dispatches that straddle a merge into
rain.metadata— which is thenormal way the workflow gets used, not an exotic one.
Why a consuming repo cannot fix this on its own
The consumer's only handle is
nix develop --command subgraph-deploy. Theversion is computed inside the task, from
git rev-parse --short HEADin theworking tree, with no argument and no environment override to reach it.
The three consumer-side workarounds all fail:
GOLDSKY_SUBGRAPH_NAME. The consumer doesset that variable, but it is the deployed subgraph's public identity — the
thing consumers query as
metaboard-base. Varying it per source revisionpublishes a new subgraph per revision instead of a new version of one.
HEAD. The consumer could commit the fetched source locally sorev-parsechanges. That yields a version naming a commit that exists in norepository, and it is only deterministic if commit metadata is pinned too —
otherwise a legitimate re-dispatch of the identical thing gets a fresh version
and re-deploys, which is the idempotency the skip exists to provide.
metadata-ref. Does not close it, per the note above:the default is a moving ref.
Options
subgraph-deploy, or anenv var (
SUBGRAPH_DEPLOY_VERSION_SUFFIX) that the task folds in when set.Smallest, and it gives the consumer the handle rather than picking the
trade-off for them. Unset keeps today's string exactly.
subgraph/tree (post-codegen, pre-build) instead of asking git. This isthe one that is correct by construction — it does not care which repo the
files came from — but it changes every existing version string org-wide, so
it is a bigger call than it looks.
<address>-<commit>-<source-rev>wherea source rev is supplied and
<address>-<commit>where it is not.(1) and (3) are the same change seen from either end; (2) is the one that stops
this recurring for the next consumer that splits its subgraph.
Not claimed
No wrong data has been served. The failure mode is a deploy that does not
happen: Goldsky keeps serving the previously deployed subgraph and the run
reports success, because "already deployed, skipping" is a success. It is
visible in the consumer's run summary today only because
rain.metadata.deploy#4 spells the empty case out; nothing in rainix says it.