-
Notifications
You must be signed in to change notification settings - Fork 212
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
implement initial per-vat meter, vatAdmin meter object #3308
Labels
enhancement
New feature or request
metering
charging for execution (was: package: tame-metering and transform-metering)
SwingSet
package: SwingSet
Milestone
Comments
warner
added
enhancement
New feature or request
SwingSet
package: SwingSet
metering
charging for execution (was: package: tame-metering and transform-metering)
labels
Jun 12, 2021
@mhofman we can pair-program on this next week |
warner
added a commit
that referenced
this issue
Jul 12, 2021
Just the meter allocate/set/deduct/delete methods for now, no code to invoke them yet. refs #3308
warner
added a commit
that referenced
this issue
Jul 22, 2021
We no longer care about injected (Node.js) -based metering: XS is the only platform where we can reasonably+efficiently meter code. This removes the unit tests which exercised injected metering, as well as the grandchild Compartment -based escape we found and fixed. The tests are modernized slightly: using `controller.kpResolution` instead of appending strings to testLog. This is a cleanup/refactoring in preparation for #3308, but is limited to unit tests, and does not change any code behavior.
warner
added a commit
that referenced
this issue
Jul 22, 2021
This introduces the "meterID" and the "meter record": a pair of Nats (`remaining` and `threshold`). kernelKeeper functions are added to create and manipulate them, and test-state.js is enhanced to exercise these. refs #3308
warner
added a commit
that referenced
this issue
Jul 22, 2021
This introduces user-visible Meter objects, and allows new dynamic vats to be bound to a single Meter, both exposed through the vatAdmin facet. Meters are long-term reservoirs of execution credits, denominated in "computrons". Each bound vat will deduct credits from its meter until exhausted, at which point the vat will be terminated. This limits the long-term CPU consumption of a vat, in addition to the fixed per-crank computron limit applied to any metered vat. Meters can be refilled and queried through their API. Each meter also has a Notifier, and a configurable notification threshold: the notifier will be updated if/when the remaining credits drop below the threshold. This should allow a supervisor in userspace enough time to refill the meter before the associated vat(s) are terminated. Some notes: * The vatAdmin facet now offers `createMeter()`, which returns a `Meter` object with methods to manipulate its `remaining` and `threshold` values, as well as a `getNotifier` to subscribe to threshold-passing events. * The vatAdmin `createVat()` call now takes a `meter: Meter` option instead of `metered: boolean`. If a Meter is provided, two things happen: * Each delivery to that (XS) vat is subject to a per-crank compute limit. * Each delivery deducts the compute usage from the Meter. * Dynamic vats are now *unmetered* by default, since we need a Meter object, not just a boolean. * When a Meter's `remaining` drops below its `threshold`, the notifier is triggered with the current `remaining` value. * When a vat's Meter reaches zero, the vat is terminated, just as if it had violated the per-crank limit. * Currently the termination message (used to reject the control facet's `.done()` Promise) is different for the per-crank limit vs the Meter limit, but this may change. * Meter deductions and threshold notifications are stashed in a new 'postAbortActions' record, to make sure they happen even if the crank is aborted and all other state changes are unwound. * The vatManager `managerOptions` still use `metered: boolean`, because the vat manager doesn't know about Meters: it only need to know whether to apply the per-crank limits or not. closes #3308
warner
added a commit
that referenced
this issue
Jul 22, 2021
We no longer care about injected (Node.js) -based metering: XS is the only platform where we can reasonably+efficiently meter code. This removes the unit tests which exercised injected metering, as well as the grandchild Compartment -based escape we found and fixed. The tests are modernized slightly: using `controller.kpResolution` instead of appending strings to testLog. This is a cleanup/refactoring in preparation for #3308, but is limited to unit tests, and does not change any code behavior.
warner
added a commit
that referenced
this issue
Jul 22, 2021
This introduces the "meterID" and the "meter record": a pair of Nats (`remaining` and `threshold`). kernelKeeper functions are added to create and manipulate them, and test-state.js is enhanced to exercise these. refs #3308
warner
added a commit
that referenced
this issue
Jul 22, 2021
This introduces user-visible Meter objects, and allows new dynamic vats to be bound to a single Meter, both exposed through the vatAdmin facet. Meters are long-term reservoirs of execution credits, denominated in "computrons". Each bound vat will deduct credits from its meter until exhausted, at which point the vat will be terminated. This limits the long-term CPU consumption of a vat, in addition to the fixed per-crank computron limit applied to any metered vat. Meters can be refilled and queried through their API. Each meter also has a Notifier, and a configurable notification threshold: the notifier will be updated if/when the remaining credits drop below the threshold. This should allow a supervisor in userspace enough time to refill the meter before the associated vat(s) are terminated. Some notes: * The vatAdmin facet now offers `createMeter()`, which returns a `Meter` object with methods to manipulate its `remaining` and `threshold` values, as well as a `getNotifier` to subscribe to threshold-passing events. * The vatAdmin `createVat()` call now takes a `meter: Meter` option instead of `metered: boolean`. If a Meter is provided, two things happen: * Each delivery to that (XS) vat is subject to a per-crank compute limit. * Each delivery deducts the compute usage from the Meter. * Dynamic vats are now *unmetered* by default, since we need a Meter object, not just a boolean. * When a Meter's `remaining` drops below its `threshold`, the notifier is triggered with the current `remaining` value. * When a vat's Meter reaches zero, the vat is terminated, just as if it had violated the per-crank limit. * Currently the termination message (used to reject the control facet's `.done()` Promise) is different for the per-crank limit vs the Meter limit, but this may change. * Meter deductions and threshold notifications are stashed in a new 'postAbortActions' record, to make sure they happen even if the crank is aborted and all other state changes are unwound. * The vatManager `managerOptions` still use `metered: boolean`, because the vat manager doesn't know about Meters: it only need to know whether to apply the per-crank limits or not. closes #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
Swingset will soon change from "all dynamic vats are metered" to "dynamic vats are unmetered by default", so set "metered: true" to retain the current behavior for ZCF vats. In addition, metering is going to be removed from the "local" (Node.js) vat-worker, so "metered: true" will require "xs-worker" (xsnap) instead. This changes the `tests/swingsetTests` runners to use xs-worker. Note that this upcoming change means any application which uses Zoe to spawn ZCF vats must run with `config.defaultManagerType='xs-worker'`, otherwise vat creation will fail when Zoe asks for a metered vat. Our two primary applications (the chain and the solo machine) both do this already. refs #3518 refs #3308
This was referenced Jul 24, 2021
warner
added a commit
that referenced
this issue
Jul 24, 2021
This introduces the "meterID" and the "meter record": a pair of Nats (`remaining` and `threshold`). kernelKeeper functions are added to create and manipulate them, and test-state.js is enhanced to exercise these. refs #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
This introduces user-visible Meter objects, and allows new dynamic vats to be bound to a single Meter, both exposed through the vatAdmin facet. Meters are long-term reservoirs of execution credits, denominated in "computrons". Each bound vat will deduct credits from its meter until exhausted, at which point the vat will be terminated. This limits the long-term CPU consumption of a vat, in addition to the fixed per-crank computron limit applied to any metered vat. Meters can be refilled and queried through their API. Each meter also has a Notifier, and a configurable notification threshold: the notifier will be updated if/when the remaining credits drop below the threshold. This should allow a supervisor in userspace enough time to refill the meter before the associated vat(s) are terminated. Some notes: * The vatAdmin facet now offers `createMeter()`, which returns a `Meter` object with methods to manipulate its `remaining` and `threshold` values, as well as a `getNotifier` to subscribe to threshold-passing events. * The vatAdmin `createVat()` call now takes a `meter: Meter` option instead of `metered: boolean`. If a Meter is provided, two things happen: * Each delivery to that (XS) vat is subject to a per-crank compute limit. * Each delivery deducts the compute usage from the Meter. * Dynamic vats are now *unmetered* by default, since we need a Meter object, not just a boolean. * When a Meter's `remaining` drops below its `threshold`, the notifier is triggered with the current `remaining` value. * When a vat's Meter reaches zero, the vat is terminated, just as if it had violated the per-crank limit. * Currently the termination message (used to reject the control facet's `.done()` Promise) is different for the per-crank limit vs the Meter limit, but this may change. * Meter deductions and threshold notifications are stashed in a new 'postAbortActions' record, to make sure they happen even if the crank is aborted and all other state changes are unwound. * The vatManager `managerOptions` still use `metered: boolean`, because the vat manager doesn't know about Meters: it only need to know whether to apply the per-crank limits or not. closes #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
When #3308 lands, the definition of "metering" will change. Previously, a "metered dynamic vat" just meant that 1: each crank was limited to some fixed amount of computrons, and 2: the low-level `deliveryResult` included the number of computrons consumed by that delivery (but nobody paid attention to the value). In the new system, metering requires a Meter: an object with a `remaining` capacity, which is deducted by the consumption of each delivery. Each metered vat is associated with (exactly one) Meter. The presence of a Meter still implies a per-crank limit: unmetered vats do not have a per-crank limit either. Previously, all dynamic vats were metered by default. Since the new metering needs a Meter, it would be awkward to retain this default: we'd have to allocate a new Meter during `createVat` without the caller's awareness, and then there would be nobody to pay attention to it (or refill it when it runs low). So this commit changes the default to "false". Dynamic vats will be unmetered (neither per-crank limits nor cumulative limits) by default. Callers who want to retain the per-crank limits should add `metered: true` to their `createVat` options. Such callers will need to create a Meter object once #3308 is done. The commit also updates swingset metering tests to add `metered: true`. refs #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
This introduces user-visible Meter objects, and allows new dynamic vats to be bound to a single Meter, both exposed through the vatAdmin facet. Meters are long-term reservoirs of execution credits, denominated in "computrons". Each bound vat will deduct credits from its meter until exhausted, at which point the vat will be terminated. This limits the long-term CPU consumption of a vat, in addition to the fixed per-crank computron limit applied to any metered vat. Meters can be refilled and queried through their API. Each meter also has a Notifier, and a configurable notification threshold: the notifier will be updated if/when the remaining credits drop below the threshold. This should allow a supervisor in userspace enough time to refill the meter before the associated vat(s) are terminated. Some notes: * The vatAdmin facet now offers `createMeter()`, which returns a `Meter` object with methods to manipulate its `remaining` and `threshold` values, as well as a `getNotifier` to subscribe to threshold-passing events. * The vatAdmin `createVat()` call now takes a `meter: Meter` option instead of `metered: boolean`. If a Meter is provided, two things happen: * Each delivery to that (XS) vat is subject to a per-crank compute limit. * Each delivery deducts the compute usage from the Meter. * When a Meter's `remaining` drops below its `threshold`, the notifier is triggered with the current `remaining` value. The actual Meter's value might have changed by the time the subscriber hears about the update. * When a vat's Meter reaches zero, the vat is terminated, just as if it had violated the per-crank limit. * Currently the termination message (used to reject the control facet's `.done()` Promise) is different for the per-crank limit vs the Meter limit, but this may change. * Meter deductions and threshold notifications are stashed in a new 'postAbortActions' record, to make sure they happen even if the crank is aborted and all other state changes are unwound. * The vatManager `managerOptions` still use `metered: boolean`, because the vat manager doesn't know about Meters: it only need to know whether to apply the per-crank limits or not. closes #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
This introduces the "meterID" and the "meter record": a pair of Nats (`remaining` and `threshold`). kernelKeeper functions are added to create and manipulate them, and test-state.js is enhanced to exercise these. refs #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
This introduces user-visible Meter objects, and allows new dynamic vats to be bound to a single Meter, both exposed through the vatAdmin facet. Meters are long-term reservoirs of execution credits, denominated in "computrons". Each bound vat will deduct credits from its meter until exhausted, at which point the vat will be terminated. This limits the long-term CPU consumption of a vat, in addition to the fixed per-crank computron limit applied to any metered vat. Meters can be refilled and queried through their API. Each meter also has a Notifier, and a configurable notification threshold: the notifier will be updated if/when the remaining credits drop below the threshold. This should allow a supervisor in userspace enough time to refill the meter before the associated vat(s) are terminated. Some notes: * The vatAdmin facet now offers `createMeter()`, which returns a `Meter` object with methods to manipulate its `remaining` and `threshold` values, as well as a `getNotifier` to subscribe to threshold-passing events. * It also offers `createUnlimitedMeter()`, which never deducts. * The vatAdmin `createVat()` call now takes a `meter: Meter` option instead of `metered: boolean`. If a Meter is provided, two things happen: * Each delivery to that (XS) vat is subject to a per-crank compute limit. * Each delivery deducts the compute usage from the Meter. * When a Meter's `remaining` drops below its `threshold`, the notifier is triggered with the current `remaining` value. The actual Meter's value might have changed by the time the subscriber hears about the update. * When a vat's Meter reaches zero, the vat is terminated, just as if it had violated the per-crank limit. * Currently the termination message (used to reject the control facet's `.done()` Promise) is different for the per-crank limit vs the Meter limit, but this may change. * Meter deductions and threshold notifications are stashed in a new 'postAbortActions' record, to make sure they happen even if the crank is aborted and all other state changes are unwound. * The vatManager `managerOptions` still use `metered: boolean`, because the vat manager doesn't know about Meters: it only need to know whether to apply the per-crank limits or not. closes #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
Zoe and spawner use `createVat()` to do their jobs. Previously, they used `{ metered: true }`, which provided a per-crank limit but not cumulative limit. This changes both to use `createUnlimitedMeter()`, then pass that as a `{ meter }` option, to achieve the same effect. When Zoe is ready to maintain (and refill) a Meter, change that to use `createMeter()` instead of `createUnlimitedMeter()`. This also adds `createMeter` and `createUnlimitedMeter` methods to the fake vatAdmin objects used by the zoe/spawner/pegasus unit tests, so the new Zoe/spawner code will work against the mocks. refs #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
This introduces user-visible Meter objects, and allows new dynamic vats to be bound to a single Meter, both exposed through the vatAdmin facet. Meters are long-term reservoirs of execution credits, denominated in "computrons". Each bound vat will deduct credits from its meter until exhausted, at which point the vat will be terminated. This limits the long-term CPU consumption of a vat, in addition to the fixed per-crank computron limit applied to any metered vat. Meters can be refilled and queried through their API. Each meter also has a Notifier, and a configurable notification threshold: the notifier will be updated if/when the remaining credits drop below the threshold. This should allow a supervisor in userspace enough time to refill the meter before the associated vat(s) are terminated. See `docs/metering.md` for documentation. Some notes: * The vatAdmin facet now offers `createMeter()`, which returns a `Meter` object with methods to manipulate its `remaining` and `threshold` values, as well as a `getNotifier` to subscribe to threshold-passing events. * It also offers `createUnlimitedMeter()`, which never deducts. * The vatAdmin `createVat()` call now takes a `meter: Meter` option instead of `metered: boolean`. If a Meter is provided, two things happen: * Each delivery to that (XS) vat is subject to a per-crank compute limit. * Each delivery deducts the compute usage from the Meter. * When a Meter's `remaining` drops below its `threshold`, the notifier is triggered with the current `remaining` value. The actual Meter's value might have changed by the time the subscriber hears about the update. * When a vat's Meter reaches zero, the vat is terminated, just as if it had violated the per-crank limit. * Currently the termination message (used to reject the control facet's `.done()` Promise) is different for the per-crank limit vs the Meter limit, but this may change. * Meter deductions and threshold notifications are stashed in a new 'postAbortActions' record, to make sure they happen even if the crank is aborted and all other state changes are unwound. * The vatManager `managerOptions` still use `metered: boolean`, because the vat manager doesn't know about Meters: it only need to know whether to apply the per-crank limits or not. closes #3308
warner
added a commit
that referenced
this issue
Jul 24, 2021
Zoe and spawner use `createVat()` to do their jobs. Previously, they used `{ metered: true }`, which provided a per-crank limit but not cumulative limit. This changes both to use `createUnlimitedMeter()`, then pass that as a `{ meter }` option, to achieve the same effect. When Zoe is ready to maintain (and refill) a Meter, change that to use `createMeter()` instead of `createUnlimitedMeter()`. This also adds `createMeter` and `createUnlimitedMeter` methods to the fake vatAdmin objects used by the zoe/spawner/pegasus unit tests, so the new Zoe/spawner code will work against the mocks. refs #3308
warner
added a commit
that referenced
this issue
Jul 25, 2021
This introduces the "meterID" and the "meter record": a pair of Nats (`remaining` and `threshold`). kernelKeeper functions are added to create and manipulate them, and test-state.js is enhanced to exercise these. refs #3308
warner
added a commit
that referenced
this issue
Jul 25, 2021
Zoe and spawner use `createVat()` to do their jobs. Previously, they used `{ metered: true }`, which provided a per-crank limit but not cumulative limit. This changes both to use `createUnlimitedMeter()`, then pass that as a `{ meter }` option, to achieve the same effect. When Zoe is ready to maintain (and refill) a Meter, change that to use `createMeter()` instead of `createUnlimitedMeter()`. This also adds `createMeter` and `createUnlimitedMeter` methods to the fake vatAdmin objects used by the zoe/spawner/pegasus unit tests, so the new Zoe/spawner code will work against the mocks. refs #3308
warner
added a commit
that referenced
this issue
Jul 25, 2021
When a crank causes the compute Meter to both hit zero *and* cross the notification threshold at the same time, we need extra code to make sure the run-queue push of the notification message does not get deleted by the `abortCrank()` that unwinds the vat's side-effects. The mechanism I wrote for this worked, but was overkill. After writing a test for it, I noticed the test still passed even if I commented out the mechanism that I thought was necessary. I simplified that code (we only need to repeat the `deductMeter`, because that will take care of repeating the notification), and added the test. refs #3308
warner
added a commit
that referenced
this issue
Jul 25, 2021
add Meters, assign to dynamic vats to track compute usage See `docs/metering.md` for documentation. closes #3308
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
enhancement
New feature or request
metering
charging for execution (was: package: tame-metering and transform-metering)
SwingSet
package: SwingSet
What is the Problem Being Solved?
In #3294 (comment) we sketched out a starting point for metering vat CPU usage. The overall goal is to effectively manage the scarce resource of CPU time, and to incentivize contract authors to write efficient code. There will be various economic mechanisms at the Zoe level (and above), but the swingset portion of the task begins with some fairly basic tools: simple meters, a way to set their CPU credits, and Zoe gets full control of all values. The authorities are pretty broad, but this should be enough to get us started.
Description of the Design
kernelKeeper
, define a "meter table", which maps integer meter IDs (m${NN}
for clarity) to a pair of numbers: the remaining capacity, and the notification thresholdm${NN}.remaining
andm${NN}.notify
, whose values are integers (Nat
)remaining
andnotify
, creates the keys, and returns the new meterIDv{$vatID}.meter
which is either missing or holds a meterIDkernel.js > processQueueMessage
:remaining
and provide it as the per-crank metering limitmin
ofremaining
and the usual threshold: just because the vat has 20 minutes of CPU time available doesn't mean we want them consuming it all in a single crankremaining
counter, since they don't give any userspace a chance to run, and it would be weird to kill a vat while userspace wasn't in control. But we need to think about this some more. For now, onlymessage
andnotify
deliveries interact withremaining
.metering
fieldmeterID.remaining
remaining
againstnotify
, invoke ameterRunningLow()
function if lower (but only whenremaining
was higher/equal at the beginning of the delivery: don't spam the notifier)meterRunningLow
to enqueue a message tovatAdminVat
, just likekernel/notifyTermination.js
doesvatAdminDevice
(kernel.jsstart()
), adding to the currentpushCreateVatEvent
,stats
, andterminate
endowmentsvatAdminDevice
methods to manipulate the metersvatAdminVat
methods to create a new "meter object" (a Remoteable, which maps through aWeakMap
to the meterID), and has methods to get/set both values, and a Notifier that is triggered by themeterRunningLow
messagevatAdminVat
createVatDynamically
method to accept an options bag, with an optionalmeter
object. If provided, look up the meterID and include it in thedynamicOptions
bundleprocessCreateVat
to include the meterID in a renamedvatKeeper.setSourceAndOptions
(maybe call itsetInitialVatData({ source, options, meterID })
), which should set the kernelDB keyv${vatID}.meter
if providedWe should make new dynamic vats be unmetered by default, so existing code doesn't break. Zoe will decide which vats are worthly of infinite compute and which ones should be limited.
At this stage, we'll give Zoe complete control over the meters. Later, we can build something more sophisticated that:
refs #3103 which is the larger (swingset-centric) question of how to measure/charge-for CPU usage, and #3294 which is the user-space (at least Zoe-space) question.
SwingSet vs Agoric Architectural Considerations
SwingSet is meant to be a standalone library for running vat-shaped computation. On its own, it knows nothing about tokens or contracts. In the
agoric-sdk
tree, we define two host applications (the chain nodes, and the ag-solo/wallet processes), and then layer a number of economic tools on top of those. We'll need to find an appropriate division of responsibility between the swingset layer and everything else we add on top.I think SwingSet should know about metering, and provide authority-limited Meter objects (which manage CPU credits), but not have opinions about how the CPU credits are created or paid for. We can leverage the vatAdminVat to manage the meters, and then the question of how to pay for credits can remain inside whatever application-side code gets access to the vatAdminVat. We should provide enough support so that this application-specific layer does not need to e.g. wrap Meter objects with something less powerful. But the fact that CPU credits are purchased with RUN tokens, for example, is a deeply Agoric-centric concept, which SwingSet is better off not knowing about.
The notion of a Keeper, which can react to a meter underflowing, is pretty central, so I think that should be a part of the base SwingSet code. The auto-refill behavior of a Keeper is worth moving into the core, so we'll need to find some dividing line between a built-in "refill X computrons whenever the meter drops below threshold Y" behavior, and the fact that those X computrons must be acquired from something that knows about RUN and a current exchange rate.
Security Considerations
The ability to raise a meter's capacity (i.e. forge CPU credits) is roughly equivalent to the ability to deny service to other code, by starving them out of CPU time. The ability to reduce a meter's capacity is a much more direct way to deny service, equivalent to simply terminating the vat. Both deserve thoughtful representative and management.
The ability to read a meter's capacity also reveals noisy (but detailed) information about the computation that took place inside a vat. On a transparent public chain, this isn't a confidentiality threat, but it could create much more sensitivity to details of the JS engine or peer vat internals than we'd care for. As @mhofman pointed out, this is the vat equivalent of a timing side-channel attack. We should carefully consider whether e.g. the parent vat (who called
vatAdminVat~.createVatDynamically()
) should get to read this capacity, and/or the metered vat itself. (At present,xsnap
only reveals the meter usage at the end of the delivery, but I can imagine us wanting to change that, and we should consider these issues carefully before doing so).Test Plan
test-state.js
should exercise the creation and manipulation of meters by kernelKeeper methodstest/vat-admin/test-innerVat.js
) should exercise both with- and without- meterstest/vat-admin/
tests should exercise theMeter
objects provided byvatAdminVat
and the methods which it offers to manipulate themtest/metering/test-dynamic-vat-metered.js
) should add some code to observe theremaining
value being reduced after successful deliveries, and the firing of the Notifier if/when the meter drops below the configured notification threshold (for the first time)The text was updated successfully, but these errors were encountered: