Skip to content

Commit

Permalink
fix: move markAsBackground to EdgeRuntime.waitUntil (#454)
Browse files Browse the repository at this point in the history
* fix: move `markAsBackground` to `EdgeRuntime.waitUntil`

* chore: fill global definitions as far as possible

* stamp: align unit tests
  • Loading branch information
nyannyacha authored Nov 28, 2024
1 parent 3cddc61 commit 954ab6d
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 18 deletions.
39 changes: 32 additions & 7 deletions crates/base/src/deno_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,7 @@ mod test {
use anyhow::Context;
use deno_config::JsxImportSourceConfig;
use deno_core::error::AnyError;
use deno_core::v8::GetPropertyNamesArgs;
use deno_core::{serde_json, serde_v8, v8, FastString, ModuleCodeString, PollEventLoopOptions};
use sb_fs::s3_fs::S3FsConfig;
use sb_fs::tmp_fs::TmpFsConfig;
Expand Down Expand Up @@ -2007,17 +2008,19 @@ mod test {
let global = context.global(inner_scope);
let edge_runtime_key: v8::Local<v8::Value> =
serde_v8::to_v8(inner_scope, "EdgeRuntime").unwrap();
assert!(!global
.get(inner_scope, edge_runtime_key)
.unwrap()
.is_undefined(),);

let edge_runtime_ns = global.get(inner_scope, edge_runtime_key).unwrap();

assert!(!edge_runtime_ns.is_undefined());
}
}

// User Runtime Should not have access to EdgeRuntime
// User Runtime can access EdgeRuntime, but only with specific APIs.
#[tokio::test]
#[serial]
async fn test_user_runtime_creation() {
let allowed_apis = vec!["waitUntil"];

let mut runtime = RuntimeBuilder::new()
.set_worker_runtime_conf(WorkerRuntimeOpts::UserWorker(Default::default()))
.build()
Expand All @@ -2030,10 +2033,32 @@ mod test {
let global = context.global(inner_scope);
let edge_runtime_key: v8::Local<v8::Value> =
serde_v8::to_v8(inner_scope, "EdgeRuntime").unwrap();
assert!(global

let edge_runtime_ns = global
.get(inner_scope, edge_runtime_key)
.unwrap()
.is_undefined(),);
.to_object(inner_scope)
.unwrap();

let edge_runtime_ns_keys = edge_runtime_ns
.get_property_names(
inner_scope,
GetPropertyNamesArgs {
mode: v8::KeyCollectionMode::OwnOnly,
index_filter: v8::IndexFilter::SkipIndices,
..Default::default()
},
)
.unwrap();

assert_eq!(edge_runtime_ns_keys.length() as usize, allowed_apis.len());

for api in allowed_apis {
let key = serde_v8::to_v8(inner_scope, api).unwrap();
let obj = edge_runtime_ns.get(inner_scope, key).unwrap();

assert!(!obj.is_undefined());
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/base/test_cases/mark-background-task/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default {
mySlowFunction(10);
// make a promise that waits for 5s, and at the same time, notify the runtime that it should
// wait for this promise.
dispatchEvent(new MyBackgroundTaskEvent(markAsBackgroundTask(sleep(5000))));
dispatchEvent(new MyBackgroundTaskEvent(EdgeRuntime.waitUntil(sleep(5000))));
return new Response();
}
}
4 changes: 2 additions & 2 deletions crates/sb_core/js/async_hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const {
let COUNTER = 0;
const PROMISES = new Map();

function markAsBackgroundTask(maybePromise) {
function waitUntil(maybePromise) {
if (maybePromise instanceof Promise) {
ops.op_tap_promise_metrics("init");
PROMISES.set(maybePromise, ++COUNTER);
Expand All @@ -31,6 +31,6 @@ function installPromiseHook() {
}

export {
markAsBackgroundTask,
waitUntil,
installPromiseHook
}
11 changes: 7 additions & 4 deletions crates/sb_core/js/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import * as globalInterfaces from 'ext:deno_web/04_global_interfaces.js';
import { SUPABASE_ENV } from 'ext:sb_env/env.js';
import { USER_WORKER_API as ai } from 'ext:sb_ai/js/ai.js';
import 'ext:sb_ai/js/onnxruntime/cache_adapter.js';
import { markAsBackgroundTask, installPromiseHook } from 'ext:sb_core_main_js/js/async_hook.js';
import { waitUntil, installPromiseHook } from 'ext:sb_core_main_js/js/async_hook.js';
import { registerErrors } from 'ext:sb_core_main_js/js/errors.js';
import {
formatException,
Expand Down Expand Up @@ -592,10 +592,13 @@ globalThis.bootstrapSBEdge = (opts, extraCtx) => {
/// DISABLE SHARED MEMORY INSTALL MEM CHECK TIMING

if (isUserWorker) {
delete globalThis.EdgeRuntime;

ObjectDefineProperties(globalThis, {
markAsBackgroundTask: nonEnumerable(markAsBackgroundTask),
EdgeRuntime: {
value: {
waitUntil,
},
configurable: true,
},
console: nonEnumerable(
new console.Console((msg, level) => {
return ops.op_user_worker_log(msg, level > 1);
Expand Down
6 changes: 4 additions & 2 deletions crates/sb_core/js/main_worker.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { core, primordials } from 'ext:core/mod.js';

import { MAIN_WORKER_API as ai } from 'ext:sb_ai/js/ai.js';
import { SUPABASE_USER_WORKERS } from 'ext:sb_user_workers/user_workers.js';
import { applySupabaseTag } from 'ext:sb_core_main_js/js/http.js';
import { core } from 'ext:core/mod.js';

const ops = core.ops;
const { ObjectDefineProperty } = primordials;

Object.defineProperty(globalThis, 'EdgeRuntime', {
ObjectDefineProperty(globalThis, 'EdgeRuntime', {
get() {
return {
ai,
Expand Down
2 changes: 1 addition & 1 deletion examples/mark-background-task/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default {
mySlowFunction(10);
// make a promise that waits for 5s, and at the same time, notify the runtime that it should
// wait for this promise.
dispatchEvent(new MyBackgroundTaskEvent(markAsBackgroundTask(sleep(5000))));
dispatchEvent(new MyBackgroundTaskEvent(EdgeRuntime.waitUntil(sleep(5000))));
return new Response();
}
}
106 changes: 105 additions & 1 deletion types/global.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,105 @@
declare function markAsBackgroundTask<T>(promise: Promise<T>): Promise<T>;
type DecoratorType = "tc39" | "typescript" | "typescript_with_metadata";

interface JsxImportBaseConfig {
defaultSpecifier?: string | null;
module?: string | null;
baseUrl?: string | null;
}

// TODO(Nyannyacha): These two type defs will be provided later.

// deno-lint-ignore no-explicit-any
type S3FsConfig = any;

// deno-lint-ignore no-explicit-any
type TmpFsConfig = any;

interface UserWorkerFetchOptions {
signal?: AbortSignal;
}

interface UserWorkerCreateOptions {
servicePath?: string | null;
envVars?: string[][] | [string, string][] | null;
noModuleCache?: boolean | null;
importMapPath?: string | null;

forceCreate?: boolean | null;
netAccessDisabled?: boolean | null;
allowNet?: string[] | null;
allowRemoteModules?: boolean | null;
customModuleRoot?: string | null;

maybeEszip?: Uint8Array | null;
maybeEntrypoint?: string | null;
maybeModuleCode?: string | null;

memoryLimitMb?: number | null;
lowMemoryMultiplier?: number | null;
workerTimeoutMs?: number | null;
cpuTimeSoftLimitMs?: number | null;
cpuTimeHardLimitMs?: number | null;

decoratorType?: DecoratorType | null;
jsxImportSourceConfig?: JsxImportBaseConfig | null;

s3FsConfig?: S3FsConfig | null;
tmpFsConfig?: TmpFsConfig | null;

context?: { [key: string]: unknown } | null;
}

interface HeapStatistics {
totalHeapSize: number;
totalHeapSizeExecutable: number;
totalPhysicalSize: number;
totalAvailableSize: number;
totalGlobalHandlesSize: number;
usedGlobalHandlesSize: number;
usedHeapSize: number;
mallocedMemory: number;
externalMemory: number;
peakMallocedMemory: number;
}

interface RuntimeMetrics {
mainWorkerHeapStats: HeapStatistics;
eventWorkerHeapStats?: HeapStatistics;
}

interface MemInfo {
total: number;
free: number;
available: number;
buffers: number;
cached: number;
swapTotal: number;
swapFree: number;
}

declare namespace EdgeRuntime {
export namespace ai {
function tryCleanupUnusedSession(): Promise<void>;
}

class UserWorker {
constructor(key: string);

fetch(request: Request, options?: UserWorkerFetchOptions): Promise<Response>;
static create(opts: UserWorkerCreateOptions): Promise<UserWorker>;
}

export function waitUntil<T>(promise: Promise<T>): Promise<T>;
export function getRuntimeMetrics(): Promise<RuntimeMetrics>;
export function applySupabaseTag(src: Request, dest: Request): void;
export function systemMemoryInfo(): MemInfo;
export function raiseSegfault(): void;

export { UserWorker as userWorkers };
}

declare namespace Deno {
export namespace errors {
class WorkerRequestCancelled extends Error { }
}
}

0 comments on commit 954ab6d

Please sign in to comment.