Skip to content
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

feat(telemetry): export swingstore vat object metrics to datadog #8050

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

toliaqat
Copy link
Contributor

Description

Scan the swingset database for vat object imports/exports and report them to datadog.

We plan to run this tool periodically on a follower node to get telemetry for performance improvement work.

Security Considerations

Scaling Considerations

When database gets pretty large, then we might need to switch to better reading methods. Currently it takes less than 5 seconds to process around a million rows.

Documentation Considerations

Testing Considerations

Manual + unit testing

@toliaqat toliaqat removed the performance Performance related issues label Jul 18, 2023
export const makeSwingstore = db => {
const dbTool = () => {
const prepare = (strings, ...params) => {
const dml = strings.join('?');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, that's great, I knew quasiliterals could be used for this purpose but had never actually seen it done

/**
* @param {import('better-sqlite3').Database} db
*/
export const makeSwingstore = db => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's name this slightly differently, to avoid confusion with the makeSwingStore defined in @agoric/swing-store.. how about makeDB ?

@@ -0,0 +1,160 @@
import { StatsD } from 'hot-shots';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this needs to be added to the package.json, and the yarn.lock updated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it's already there. I bet the lint CI job doesn't believe in packages that aren't direct subdirectories of the top-level packages/ workspace.

Let's just disable lint on this line, I think by inserting

// eslint-disable-next-line

before it.. see if that appeases CI.


const kvAll = key => sql.iterate`select * from kvStore where key GLOB ${key}`;

return Object.freeze({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's consider using harden here.. if we're going to freeze it, we might as well go all the way. It's not so important for a CLI tool like this (this returned API object is not being exposed to any malicious/untrusted code), but it's not a bad habit to get into.

return harden({ .., and then the index.js needs to start with import '@endo/init', and we need to add @endo/init to the package.json. That's the pattern we use for our "Hardened JS" style. We usually harden everything exported by a module (export const fname = harden(() => { ..), as well as any object exposed to the importers of the module.

count += 1;
const vatID = row.key.split('.')[0];
const stat = vatStats.get(vatID) || newStat();
vatStats.set(vatID, stat);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since there are a lot of rows, this might be a tiny bit faster if we skip the redundant .set, something like

let stat = vatStats.get(vatID);
if (!stat) {
  stat = newStat();
  vatStats.set(vatID, stat);
}

const stat = vatStats.get(vatID) || newStat();
vatStats.set(vatID, stat);
stat.objectExportsReachable +=
object.test(row.key) && exportReach.test(row.value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise, there's a lot of redundant regexp execution here, I'd suggest hoisting the if (object. test(row.key)) check, as a single conditional around the block of four import/export/etc checks.

// "v$NN.c.ko48": "_ o-7" # merely-recognizable import
// "v$NN.c.kp70": "R p+12" # promise (always "R", sometimes "p+" sometimes "p-")

const object = /^v\d+\.c\..*ko\d.*$/; // vNN.c.koNNNNN*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the trailing .*$ is redundant, might consider omitting it (although I imagine the regexp compiler discards it, so it's not like it's likely to make anything slower, and it does make it nicely clear what we're expecting).

console.log(`Opening database at ${fullPath}`);
const kStore = makeSwingstore(dbOpen(fullPath, { readonly: true }));
const height = kStore.findHeight();
const vatItems = kStore.findVatItems();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe named this vatItemIterator to make it more obvious that we aren't reading the entire DB into RAM first

@@ -0,0 +1,27 @@
{
"name": "store-stats",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's name it swingset-store-vatstats, and add a private: true field just to remind us to give it a better name (something under @agoric/, probably) before we publish it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants