querysub 0.486.0 → 0.488.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.486.0",
3
+ "version": "0.488.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -89,13 +89,22 @@ class ValidStateComputer {
89
89
  }
90
90
  }
91
91
 
92
+ return true;
93
+ });
94
+
95
+ // For any value we're the authority on that we already have, swap in our own stored copy. The data is immutable per (path, time) — only the valid state can differ, and we own that — so we must never adopt a sibling authority's valid state. Remapping (rather than only skipping the re-ingest) keeps everything downstream (valuesChanged, dependenciesChanged) on a single object instead of a mix of our copy and the remote's.
96
+ pathValues = pathValues.map(value => {
97
+ if (!PathRouter.isSelfAuthority(value.path)) return value;
98
+ return authorityStorage.getValueExactMaybeRejected(value.path, value.time) || value;
99
+ });
100
+
101
+ for (let value of pathValues) {
92
102
  if (!PathRouter.isSelfAuthority(value.path)) {
93
103
  notOurValues.push(value);
94
104
  } else {
95
105
  ourValues.push(value);
96
106
  }
97
- return true;
98
- });
107
+ }
99
108
 
100
109
  // NOTE: Watch value locks, will internally watch remote locks if we aren't the authority on the values
101
110
  lockWatcher2.watchValueLocks(ourValues, now);
@@ -112,7 +121,9 @@ class ValidStateComputer {
112
121
  dependenciesChanged.add(watcher);
113
122
  }
114
123
 
115
- if (!value.valid) {
124
+ // We don't compute the valid state for values we don't own, but we still forward them to our watchers, and that must include accept transitions, not only rejects — otherwise a watcher we already forwarded a reject to stays stuck once the authority accepts. Ingest happens further below, so getValueExactMaybeRejected still returns our previous value here; only trigger when the incoming valid differs from what we currently hold, so we forward real changes without re-triggering on unchanged re-sends.
125
+ let prevValidState = authorityStorage.getValueExactMaybeRejected(value.path, value.time)?.valid;
126
+ if (prevValidState !== value.valid) {
116
127
  initialTriggers.initialTriggerNonHistoryWatchers.add(value.path);
117
128
  }
118
129
  }
@@ -182,9 +193,7 @@ class ValidStateComputer {
182
193
  }
183
194
 
184
195
  authorityStorage.ingestValues(notOurValues, { doNotArchive });
185
- let newOurValues = ourValues.filter(x => !authorityStorage.getValueExactMaybeRejected(x.path, x.time));
186
- // Only ingest the values that are new to us. Otherwise, we'd be accepting the valid state from somebody else, and we're the authority, so we just shouldn't do that.
187
- authorityStorage.ingestValues(newOurValues, { doNotArchive });
196
+ authorityStorage.ingestValues(ourValues, { doNotArchive });
188
197
 
189
198
 
190
199
  let loopCount = 0;
@@ -221,10 +230,8 @@ class ValidStateComputer {
221
230
  for (let validStateChange of validStateChanged) {
222
231
  valuesChanged.add(validStateChange);
223
232
 
224
- // NOTE: This approach should be faster than doing a full initial sync, but we'll see if it breaks anything.
225
- if (!validStateChange.valid) {
226
- initialTriggers.initialTriggerNonHistoryWatchers.add(validStateChange.path);
227
- }
233
+ // Push the latest value to this path's (possibly cross-node) watchers on ANY valid-state change, not just rejections — external watchers are only re-notified via initialTriggerNonHistoryWatchers; valuesChanged alone propagates between co-authorities and never re-notifies a watcher that already holds a stale verdict, so without notifying on accepts a reject→accept recovery is computed correctly on the authority but the watcher stays stuck on the earlier rejected value.
234
+ initialTriggers.initialTriggerNonHistoryWatchers.add(validStateChange.path);
228
235
 
229
236
  // // NOTE: As of right now, I think the only reason to force an initial trigger in this case is so that we can bust all the values through the query sub http server to the clients. Because if we just tell the HTTP server that one value has been rejected, it can go tell its clients, but that won't give them the latest value. Which isn't great. We should really have something where we can tell them. HMM...
230
237
  // // IMPORTANT! The consequence of this is that if someone predicts a value, sends it to us, it's not valid, we will see it's not valid, and we will tell all of the nodes that are watching that path about all of the values on that path. This is inefficient. However, rejected values should be quite rare, so it shouldn't be too inefficient.