-
Notifications
You must be signed in to change notification settings - Fork 215
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
collection manager clearInternal
instantiates keys during deletion
#5053
Comments
warner
added a commit
that referenced
this issue
Apr 9, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. refs #1848
warner
added a commit
that referenced
this issue
Apr 9, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. refs #1848
warner
added a commit
that referenced
this issue
Apr 9, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. refs #1848
warner
added a commit
that referenced
this issue
Apr 9, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. The collectionManager was changed to keep an in-RAM set of the vrefs for all collections, both virtual and durable. We need the virtuals to implement `deleteAllVirtualCollections` because there's no efficient way to enumerate them from the vatstore entries, and the code is a lot simpler if I just track all of them. We also need the Set to tolerate duplicate deletion attempts: `deleteAllVirtualCollections` runs first, but just afterwards a `bringOutYourDead` might notice a zero refcount on a virtual collection and attempt to delete it a second time. We cannot keep this Set in RAM: if we have a very large number of collections, it violates our RAM budget, so we need to change our DB structure to accomodate this need (#5058). refs #1848
warner
added a commit
that referenced
this issue
Apr 9, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. The collectionManager was changed to keep an in-RAM set of the vrefs for all collections, both virtual and durable. We need the virtuals to implement `deleteAllVirtualCollections` because there's no efficient way to enumerate them from the vatstore entries, and the code is a lot simpler if I just track all of them. We also need the Set to tolerate duplicate deletion attempts: `deleteAllVirtualCollections` runs first, but just afterwards a `bringOutYourDead` might notice a zero refcount on a virtual collection and attempt to delete it a second time. We cannot keep this Set in RAM: if we have a very large number of collections, it violates our RAM budget, so we need to change our DB structure to accomodate this need (#5058). refs #1848
warner
added a commit
that referenced
this issue
Apr 12, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. The collectionManager was changed to keep an in-RAM set of the vrefs for all collections, both virtual and durable. We need the virtuals to implement `deleteAllVirtualCollections` because there's no efficient way to enumerate them from the vatstore entries, and the code is a lot simpler if I just track all of them. We also need the Set to tolerate duplicate deletion attempts: `deleteAllVirtualCollections` runs first, but just afterwards a `bringOutYourDead` might notice a zero refcount on a virtual collection and attempt to delete it a second time. We cannot keep this Set in RAM: if we have a very large number of collections, it violates our RAM budget, so we need to change our DB structure to accomodate this need (#5058). refs #1848
warner
added a commit
that referenced
this issue
Apr 12, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. The collectionManager was changed to keep an in-RAM set of the vrefs for all collections, both virtual and durable. We need the virtuals to implement `deleteAllVirtualCollections` because there's no efficient way to enumerate them from the vatstore entries, and the code is a lot simpler if I just track all of them. We also need the Set to tolerate duplicate deletion attempts: `deleteAllVirtualCollections` runs first, but just afterwards a `bringOutYourDead` might notice a zero refcount on a virtual collection and attempt to delete it a second time. We cannot keep this Set in RAM: if we have a very large number of collections, it violates our RAM budget, so we need to change our DB structure to accomodate this need (#5058). refs #1848
warner
added a commit
that referenced
this issue
Apr 12, 2022
This deletes most non-durable data during upgrade. stopVat() delegates to a new function `releaseOldState()`, which makes an incomplete effort to drop everything. The portions which are complete are: * find all locally-decided promises and rejects them * find all exported Remotables and virtual objects, and abandons them * simulate finalizers for all in-RAM Presences and Representatives * use collectionManager to delete all virtual collections * perform a bringOutYourDead to clean up resulting dead references After that, `deleteVirtualObjectsWithoutDecref` walks the vatstore and deletes the data from all virtual objects, without attempting to decref the things they pointed to. This fails to release durables and imports which were referenced by those virtual objects (e.g. cycles that escaped the earlier purge). Code is written, but not yet complete, to decref those objects properly. A later update to this file will activate that (and update the tests to confirm it works). The new unit test constructs a large object graph and examines it afterwards to make sure everything was deleted appropriately. The test knows about the limitations of `deleteVirtualObjectsWithoutDecref`, as well as bug #5053 which causes some other objects to be retained incorrectly. The collectionManager was changed to keep an in-RAM set of the vrefs for all collections, both virtual and durable. We need the virtuals to implement `deleteAllVirtualCollections` because there's no efficient way to enumerate them from the vatstore entries, and the code is a lot simpler if I just track all of them. We also need the Set to tolerate duplicate deletion attempts: `deleteAllVirtualCollections` runs first, but just afterwards a `bringOutYourDead` might notice a zero refcount on a virtual collection and attempt to delete it a second time. We cannot keep this Set in RAM: if we have a very large number of collections, it violates our RAM budget, so we need to change our DB structure to accomodate this need (#5058). refs #1848
FUDCo
added a commit
that referenced
this issue
Jun 22, 2022
FUDCo
added a commit
that referenced
this issue
Jul 9, 2022
FUDCo
added a commit
that referenced
this issue
Jul 9, 2022
turadg
pushed a commit
that referenced
this issue
Jul 14, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
While working on
stopVat()
, I found that my unit test was failing to release all of the durable objects that it was supposed to. I tracked it down to a non-ideal behavior in the virtual collection manager.My test's setup included a durable collection (Map aka "Store") with a durable object as a key:
During
stopVat()
, we go through several phases of deleting references that aren't supposed to survive the upgrade, one of which walksslotToVal
to basically find all the Presences and Representatives that were held by userspace RAM, and we pretend that their finalizers have been fired. This releases all the memory pins on virtual/durable objects and on imports. Then we do a bigbringOurYourDead
that should free all of the thus-released objects. During this pass, liveslots notices the virtual collection being dropped, and usesdeleteCollection()
on it. That usesclearInternal
to delete all its entries.clearInternal
useskeys()
to iterate over all the keys, and callsdeleteInternal(key)
on each one:keys()
needs to instantiate (unserialize) each key object, because the normal userspace usage of it should return these objects (for strong maps, at least). Likewisedelete()
needs to take a key object (deleteInternal
is justdelete
without the.size -= 1
update).keys()
needs to extract the encoded key value from thekvStore
key, and unserialize that into a userspace-visible key object.delete()
takes the userspace-visible key object and encodes it into akvStore
key.So the act of deleting the collection causes the (re)creation of a bunch of Representatives for any virtual objects that were used as keys.
In my
stopVat
case, this instantiation ofdur23
occurs after we've already scannedslotToVal
and manually triggered the finalizer for each. The cleanup code is left with adur23
Representative in RAM, after the lastbringOutYourDead
has been called, so it never gets reaped, and the data fordur23
is not deleted as it should be. This was complicated by the virtual-object cache, which despite asize = 0
still holds on to the last Representative that was created. My test builds some dummy ones to help flush the cache, but userspace does not (must not) get control afterstopVat
begins, so there's no opportunity for my test to make a dummy object after thedur23
Representative was re-instantiated. Even if I managed an additionalbringOutYourDead
, that vref would be pinned by the RAM pillar of the Representative.The three problems of this approach to
clearInternal
are:gcAndFinalize
, so we'll hold on to the Remotable (and anything it holds) for too longstopVat
to delete everythingFaster Approach
clearInternal
does not really need to instantiate the keys that it's about to delete: it only does that becausekeys()
is a conveniently-available mechanism to iterate over the keys, anddelete
is a conveniently-available mechanism to delete an entry (which requires an instantiated key).The better approach (albeit not as tidy) would use an internal flavor of
keys
that only returns the encodedkvStore
key, and an internal flavor ofdelete
which accepts an encoded key instead of a key object. This would avoid instantiating the object, and would be faster as well.The overall refactoring would be something like:
Alternate stopVat approaches
I've explored ways to work around this re-instantiation behavior in
stopVat()
, but I'm not optimistic.deleteCollection
andclearInternal
anddeleteInternal
all have adoMoreGC
flag to report that they just dropped a Remotable, because that means a subsequentgcAndFinalize
might yield some more drops (things held by their RAM pillar). OurscanForDeadObjects
keeps looping aroundgcAndFinalize
and processing the deadset until this flag comes back false (andpossiblyDeadSet
is empty).But they don't know that instantiating key objects might also necessitate another round of
bringOutYourDead
: the flag is returnedfalse
unless specifically a Remotable was dropped.So one fix might be to change
deleteCollection
to always returntrue
. A less busy solution would be fordeleteInternal
to always returntrue
(that would avoid the churn for empty collections; not very interesting). Even less busy would be ifclearInternal
examinedk
(the key object) and setdoMoreGC
if it was a Remotable or a Representative: that would fix the first problem (the missinggcAndFinalize
loop), but not the other two.The cache retention problem means that the Representative might be retained in RAM anyways, and stopVat really needs to clear out everything. My
finalizeEverything
tool does that (the one that scansslotToVal
and triggers all the finalizers, to shake loose the Representatives for the key objects), but I need to know when to call it, especially how many times to call it. I currently call it once, followed bybringOutYourDead
. But ifbringOutYourDead
causes deletion, and deletion creates new RAM objects, then I must callfinalizeEverything
again, and thenbringOutYourDead
again, and the only way to know when it is safe to stop is to watch for thepossiblyDeadSet
to be empty.stopVat()
lives outsidescanForDeadObjects
, not inside, so it's not really in a good position to manage this.The text was updated successfully, but these errors were encountered: