mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
Each of these was checked against the live database before it moved,
because a member name is free to rename and a stored value is not.
Members only -- the value never said hog, so nothing in Postgres changes:
TaggerType.HOG / EvaluationType.HOG the column already holds `script`;
`HOG = "script", "Script"` was a
member disagreeing with its own value
FEATURE_FLAGS.HOG the flag key `script` is read by the
flag service and does not move
HEDGEFN_SKIN_SPIDERHOG same, key `mascot-skin-spiderscript`
_HOGLAND_MARKER already `/var/lib/script`; renamed to
_BOX_MARKER, which is what the
function returns and what the comment
beside it already called it
Value too, having established nothing stores it: `InsightType.HOG = 'HOG'`
is derived from `query.kind === 'ScriptQuery'`, never written -- the one
writer, queryNodeToFilter, has no ScriptQuery entry, and zero of the 28
insight rows carry it in `filters.insight`.
Two readers keyed on that value had to move with it, and neither would
have been caught by renaming the enum alone: INSIGHT_TYPE_URLS spells
`HOG:` as a plain key while its siblings are computed, and the Slack
unfurl map keys the same string in Python.
The rest is copy a person reads -- "HOG code exceeds maximum size" is an
API error message, not an identifier.
Left alone deliberately: `ReviewHog` and `HogDesk`, which survive only in
applied migrations' `choices=` lists. The live members are already
`REVIEW = "review"` and `Desk = "desk"`, and Django compares choices in
Python without ever writing them, so those lists are a snapshot of a
field that has not looked like that for some time.
Co-authored-by: Hanzo Dev <[email protected]>
147 lines
5.3 KiB
JavaScript
147 lines
5.3 KiB
JavaScript
const assert = require('assert')
|
|
const cyclotron = require('../.')
|
|
const crypto = require('crypto')
|
|
|
|
// Set of available job states
|
|
const JOB_STATES = Object.freeze({
|
|
AVAILABLE: 'available',
|
|
RUNNING: 'running',
|
|
FAILED: 'failed',
|
|
COMPLETED: 'completed',
|
|
})
|
|
|
|
const AVAILABLE_WORKERS = Object.freeze({
|
|
FETCH: 'fetch',
|
|
SCRIPT: 'script',
|
|
})
|
|
|
|
async function main() {
|
|
let poolConfig = {
|
|
db_url: 'postgresql://insights:insights@localhost:5432/cyclotron',
|
|
}
|
|
|
|
let managerConfig = {
|
|
shards: [poolConfig],
|
|
}
|
|
|
|
// Most processes will only need to do one of these, but we can do both here for demonstration purposes
|
|
await cyclotron.initWorker(JSON.stringify(poolConfig))
|
|
await cyclotron.initManager(JSON.stringify(managerConfig))
|
|
|
|
// Maybe inits won't throw on re-calling, and are also short-circuiting to be almost free, so safe to call frequently
|
|
// (although I still wouldn't call them in a loop)
|
|
await cyclotron.maybeInitWorker(JSON.stringify(poolConfig))
|
|
await cyclotron.maybeInitManager(JSON.stringify(managerConfig))
|
|
|
|
let five_mintes_ago = new Date(new Date().getTime() - 5 * 60000).toISOString()
|
|
let queue_name = 'default'
|
|
|
|
let job_1 = {
|
|
team_id: 1,
|
|
queue_name,
|
|
priority: 0,
|
|
scheduled: five_mintes_ago,
|
|
function_id: crypto.randomUUID(), // Is nullable
|
|
vm_state: null,
|
|
parameters: null,
|
|
blob: null,
|
|
metadata: null,
|
|
}
|
|
|
|
let job_2 = {
|
|
team_id: 1,
|
|
queue_name,
|
|
priority: 1,
|
|
scheduled: five_mintes_ago,
|
|
function_id: crypto.randomUUID(), // Is nullable
|
|
vm_state: null,
|
|
parameters: null,
|
|
blob: null,
|
|
metadata: null,
|
|
}
|
|
|
|
await cyclotron.createJob(JSON.stringify(job_1))
|
|
await cyclotron.createJob(JSON.stringify(job_2))
|
|
|
|
// Jobs (as well as any other 'complex' data shape) are serialized across the API boundary,
|
|
// because that's (according to the neon maintainers) /actually faster/ than doing a bunch
|
|
// of cross-runtime pointer chasing.
|
|
let jobs = JSON.parse(await cyclotron.dequeueJobs(queue_name, 2))
|
|
assert(jobs.length === 2)
|
|
assert(jobs[0].function_id === job_1.function_id)
|
|
assert(jobs[1].function_id === job_2.function_id)
|
|
|
|
job_1 = jobs[0]
|
|
job_2 = jobs[1]
|
|
|
|
// All of these throw if the job hasn't been dequeued by the worker created when init_worker was called,
|
|
// or if there's some serde error - generally, interacting with the cyclotron should involve try/catch in
|
|
// some far outer catch. We can iterate on this API to make it more ergonomic with time, but
|
|
// my js/ts is... rusty (co-pilot wrote this joke)
|
|
cyclotron.setState(job_1.id, JOB_STATES.AVAILABLE)
|
|
cyclotron.setState(job_2.id, JOB_STATES.AVAILABLE)
|
|
|
|
cyclotron.setQueue(job_1.id, 'non-default')
|
|
cyclotron.setQueue(job_2.id, 'non-default')
|
|
|
|
// Priority is lowest-first, so this means we can assert that job_2 will be returned first on subsequent dequeue_jobs
|
|
cyclotron.setPriority(job_1.id, 2)
|
|
cyclotron.setPriority(job_2.id, 1)
|
|
|
|
let ten_minutes_ago = new Date(new Date().getTime() - 10 * 60000).toISOString()
|
|
cyclotron.setScheduledAt(job_1.id, ten_minutes_ago)
|
|
cyclotron.setScheduledAt(job_2.id, ten_minutes_ago)
|
|
|
|
cyclotron.setVmState(job_1.id, JSON.stringify({ state: 'running' }))
|
|
cyclotron.setVmState(job_2.id, JSON.stringify({ state: 'running' }))
|
|
|
|
cyclotron.setParameters(job_1.id, JSON.stringify({ parameters: 'running' }))
|
|
cyclotron.setParameters(job_2.id, JSON.stringify({ parameters: 'running' }))
|
|
|
|
cyclotron.setMetadata(job_1.id, JSON.stringify({ metadata: 'running' }))
|
|
cyclotron.setMetadata(job_2.id, JSON.stringify({ metadata: 'running' }))
|
|
|
|
// Flush the updates queued up above back to the queue. Subsequent calls to flush
|
|
// will throw if a job isn't re-acquired. Flushes will fail if a job state update
|
|
// isn't included (workers should not purposefully leave jobs in a running state)
|
|
await cyclotron.flushJob(job_1.id)
|
|
await cyclotron.flushJob(job_2.id)
|
|
|
|
jobs = JSON.parse(await cyclotron.dequeueWithVmState('non-default', 2))
|
|
|
|
assert(jobs[0].id == job_2.id)
|
|
assert(jobs[1].id == job_1.id)
|
|
|
|
assert(jobs[0].function_id === job_2.function_id)
|
|
assert(jobs[1].function_id === job_1.function_id)
|
|
|
|
assert(jobs[0].team_id === job_2.team_id)
|
|
assert(jobs[1].team_id === job_1.team_id)
|
|
|
|
assert(jobs[0].queue_name === 'non-default')
|
|
assert(jobs[1].queue_name === 'non-default')
|
|
|
|
assert(jobs[0].priority === 1)
|
|
assert(jobs[1].priority === 2)
|
|
|
|
assert(jobs[0].scheduled === ten_minutes_ago)
|
|
assert(jobs[1].scheduled === ten_minutes_ago)
|
|
|
|
assert(jobs[0].vm_state === JSON.stringify({ state: 'running' }))
|
|
assert(jobs[1].vm_state === JSON.stringify({ state: 'running' }))
|
|
assert(jobs[0].parameters === JSON.stringify({ parameters: 'running' }))
|
|
assert(jobs[1].parameters === JSON.stringify({ parameters: 'running' }))
|
|
assert(jobs[0].metadata === JSON.stringify({ metadata: 'running' }))
|
|
assert(jobs[1].metadata === JSON.stringify({ metadata: 'running' }))
|
|
|
|
// Now we'll mark these jobs as completed
|
|
cyclotron.setState(job_1.id, JOB_STATES.COMPLETED)
|
|
cyclotron.setState(job_2.id, JOB_STATES.COMPLETED)
|
|
|
|
// And flush them back to the queue
|
|
await cyclotron.flushJob(job_1.id)
|
|
await cyclotron.flushJob(job_2.id)
|
|
}
|
|
|
|
main()
|