-
Notifications
You must be signed in to change notification settings - Fork 0
Fix supervisor: report vault execution so stuck-scan order isn't fixed #187
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,3 +22,4 @@ | |
| [submodule "lib/FlowALP"] | ||
| path = lib/FlowALP | ||
| url = git@github.com:onflow/FlowALP.git | ||
| branch = v0 | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -58,6 +58,11 @@ access(all) contract FlowYieldVaultsSchedulerRegistry { | |||||||||
| /// Stored as a dictionary for O(1) add/remove; iteration gives the pending set | ||||||||||
| access(self) var pendingQueue: {UInt64: Bool} | ||||||||||
|
|
||||||||||
| /// Order for stuck scanning: least recently reported (executed) first. | ||||||||||
| /// Vaults call reportExecution() on each run (remove id from array, append to end). | ||||||||||
| /// Supervisor scans only the first MAX_BATCH_SIZE entries for stuck detection. | ||||||||||
| access(self) var stuckScanOrder: [UInt64] | ||||||||||
|
|
||||||||||
| /* --- ACCOUNT-LEVEL FUNCTIONS --- */ | ||||||||||
|
|
||||||||||
| /// Register a YieldVault and store its handler and schedule capabilities (idempotent) | ||||||||||
|
|
@@ -73,9 +78,27 @@ access(all) contract FlowYieldVaultsSchedulerRegistry { | |||||||||
| self.yieldVaultRegistry[yieldVaultID] = true | ||||||||||
| self.handlerCaps[yieldVaultID] = handlerCap | ||||||||||
| self.scheduleCaps[yieldVaultID] = scheduleCap | ||||||||||
| self.stuckScanOrder.append(yieldVaultID) | ||||||||||
| emit YieldVaultRegistered(yieldVaultID: yieldVaultID) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// Called by the account that holds this contract (e.g. from the wrapper) on every execution. Removes yieldVaultID from stuckScanOrder (if present) | ||||||||||
| /// and appends it to the end so the Supervisor only scans the first N (least recently executed) for stuck. | ||||||||||
| access(account) fun reportExecution(yieldVaultID: UInt64) { | ||||||||||
| if !(self.yieldVaultRegistry[yieldVaultID] ?? false) { | ||||||||||
| return | ||||||||||
| } | ||||||||||
| var i = 0 | ||||||||||
| while i < self.stuckScanOrder.length { | ||||||||||
| if self.stuckScanOrder[i] == yieldVaultID { | ||||||||||
| self.stuckScanOrder.remove(at: i) | ||||||||||
| break | ||||||||||
| } | ||||||||||
| i = i + 1 | ||||||||||
| } | ||||||||||
| self.stuckScanOrder.append(yieldVaultID) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// Adds a yield vault to the pending queue for seeding by the Supervisor | ||||||||||
| access(account) fun enqueuePending(yieldVaultID: UInt64) { | ||||||||||
| if self.yieldVaultRegistry[yieldVaultID] == true { | ||||||||||
|
|
@@ -92,12 +115,20 @@ access(all) contract FlowYieldVaultsSchedulerRegistry { | |||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// Unregister a YieldVault (idempotent) - removes from registry, capabilities, and pending queue | ||||||||||
| /// Unregister a YieldVault (idempotent) - removes from registry, capabilities, pending queue, and stuckScanOrder | ||||||||||
| access(account) fun unregister(yieldVaultID: UInt64) { | ||||||||||
| self.yieldVaultRegistry.remove(key: yieldVaultID) | ||||||||||
| self.handlerCaps.remove(key: yieldVaultID) | ||||||||||
| self.scheduleCaps.remove(key: yieldVaultID) | ||||||||||
| let pending = self.pendingQueue.remove(key: yieldVaultID) | ||||||||||
| var i = 0 | ||||||||||
| while i < self.stuckScanOrder.length { | ||||||||||
| if self.stuckScanOrder[i] == yieldVaultID { | ||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||||||||||
| self.stuckScanOrder.remove(at: i) | ||||||||||
| break | ||||||||||
| } | ||||||||||
| i = i + 1 | ||||||||||
| } | ||||||||||
| emit YieldVaultUnregistered(yieldVaultID: yieldVaultID, wasInPendingQueue: pending != nil) | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
@@ -156,19 +187,19 @@ access(all) contract FlowYieldVaultsSchedulerRegistry { | |||||||||
| /// Get paginated pending yield vault IDs | ||||||||||
| /// @param page: The page number (0-indexed) | ||||||||||
| /// @param size: The page size (defaults to MAX_BATCH_SIZE if nil) | ||||||||||
| access(all) view fun getPendingYieldVaultIDsPaginated(page: Int, size: Int?): [UInt64] { | ||||||||||
| let pageSize = size ?? self.MAX_BATCH_SIZE | ||||||||||
| access(all) view fun getPendingYieldVaultIDsPaginated(page: Int, size: UInt?): [UInt64] { | ||||||||||
| let pageSize = size ?? Int(self.MAX_BATCH_SIZE) | ||||||||||
|
Comment on lines
+190
to
+191
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| let allPending = self.pendingQueue.keys | ||||||||||
| let startIndex = page * pageSize | ||||||||||
| let startIndex = page * Int(pageSize) | ||||||||||
|
|
||||||||||
| if startIndex >= allPending.length { | ||||||||||
| return [] | ||||||||||
| } | ||||||||||
| let endIndex = startIndex + pageSize > allPending.length | ||||||||||
| ? allPending.length | ||||||||||
| : startIndex + pageSize | ||||||||||
|
|
||||||||||
| let endIndex = startIndex + Int(pageSize) > allPending.length | ||||||||||
| ? allPending.length | ||||||||||
| : startIndex + Int(pageSize) | ||||||||||
|
|
||||||||||
| return allPending.slice(from: startIndex, upTo: endIndex) | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
@@ -177,6 +208,14 @@ access(all) contract FlowYieldVaultsSchedulerRegistry { | |||||||||
| return self.pendingQueue.length | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// Returns the first n yield vault IDs from the stuck-scan order (least recently executed first). | ||||||||||
| /// Supervisor should only scan these for stuck detection instead of all registered vaults. | ||||||||||
| /// @param limit: Maximum number of IDs to return (caller typically passes MAX_BATCH_SIZE) | ||||||||||
| access(all) view fun getStuckScanCandidates(limit: UInt): [UInt64] { | ||||||||||
| let end = limit > UInt(self.stuckScanOrder.length) ? self.stuckScanOrder.length : limit | ||||||||||
| return self.stuckScanOrder.slice(from: 0, upTo: Int(end)) | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /// Get global Supervisor capability, if set | ||||||||||
| /// NOTE: Access restricted - only used internally by the scheduler | ||||||||||
| access(account) | ||||||||||
|
|
@@ -193,6 +232,7 @@ access(all) contract FlowYieldVaultsSchedulerRegistry { | |||||||||
| self.handlerCaps = {} | ||||||||||
| self.scheduleCaps = {} | ||||||||||
| self.pendingQueue = {} | ||||||||||
| self.stuckScanOrder = [] | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,7 @@ import "FlowYieldVaultsSchedulerRegistry" | |
| /// Returns a paginated list of yield vault IDs in the pending queue. | ||
| /// @param page: The page number (0-indexed) | ||
| /// @param size: The number of yield vaults per page (defaults to MAX_BATCH_SIZE if 0) | ||
| access(all) fun main(page: Int, size: Int): [UInt64] { | ||
| let pageSize: Int? = size > 0 ? size : nil | ||
| return FlowYieldVaultsSchedulerRegistry.getPendingYieldVaultIDsPaginated(page: page, size: pageSize) | ||
| access(all) fun main(page: Int, size: UInt): [UInt64] { | ||
| return FlowYieldVaultsSchedulerRegistry.getPendingYieldVaultIDsPaginated(page: page, size: size) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Either update |
||
| } | ||
|
|
||
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.
You can use
firstIndex(of: T): Int?to find the element herehttps://cadence-lang.org/docs/language/values-and-types/arrays#array-fields-and-functions