Skip to content

Commit

Permalink
Merge 7e5980f into 6c201e2
Browse files Browse the repository at this point in the history
  • Loading branch information
kraenhansen committed Mar 23, 2024
2 parents 6c201e2 + 7e5980f commit 30561b0
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* Improved performance of RQL queries on a non-linked string property using `>`, `>=`, `<`, `<=` operators and fixed behavior that a null string should be evaluated as less than everything, previously nulls were not matched. ([realm/realm-core#3939](https://github.com/realm/realm-core/issues/3939))
* Added support for using aggregate operations on Mixed properties in queries. ([realm/realm-core#7398](https://github.com/realm/realm-core/pull/7398))
* Improved file compaction performance on platforms with page sizes greater than 4k (for example arm64 Apple platforms) for files less than 256 pages in size. ([realm/realm-core#7492](https://github.com/realm/realm-core/pull/7492))
* Added a static `Realm.shutdown()` method, which closes all Realms, cancels all pending `Realm.open` calls, clears internal caches, resets the logger and collects garbage. Call this method to free up the event loop and allow Node.js to perform a graceful exit. ([#6571](https://github.com/realm/realm-js/pull/6571), since v12.0.0)

### Fixed
* Aligned Dictionaries to Lists and Sets when they get cleared. ([#6205](https://github.com/realm/realm-core/issues/6205), since v10.3.0-rc.1)
Expand Down
49 changes: 41 additions & 8 deletions integration-tests/tests/src/node/clean-exit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,49 @@
//
////////////////////////////////////////////////////////////////////////////

import { execSync } from "child_process";
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import { assert } from "chai";
import module from "node:module";

describe("Clean exit for Node.js scripts", function () {
const require = module.createRequire(import.meta.url);
const realmPackagePath = require.resolve("realm");
assert(realmPackagePath, "Expected to resolve Realm package");

function expectCleanExit(source: string, timeout: number) {
execFileSync(process.execPath, ["--eval", source], {
timeout,
cwd: fs.mkdtempSync(path.join(os.tmpdir(), "realm-exit-test-")),
stdio: "inherit",
env: {
REALM_PACKAGE_PATH: realmPackagePath,
},
});
}

describe("clean exits in Node.js", function () {
// Repro for https://github.com/realm/realm-js/issues/4535 - currently still failing
it.skip("exits cleanly when creating a new Realm.App", function (this: RealmContext) {
execSync(
`node -e 'const Realm = require("realm"); const app = new Realm.App({ id: "myapp-abcde" }); Realm.clearTestState();'`,
{
timeout: Math.min(this.timeout(), 5000),
},
it("exits when creating Realm", function (this: RealmContext) {
expectCleanExit(
`
const Realm = require(process.env.REALM_PACKAGE_PATH);
const realm = new Realm();
Realm.shutdown();
`,
Math.min(this.timeout(), 5000),
);
});

it("exits when creating Realm.App", function (this: RealmContext) {
expectCleanExit(
`
const Realm = require(process.env.REALM_PACKAGE_PATH);
new Realm.App({ id: "myapp-abcde" });
Realm.shutdown();
`,
Math.min(this.timeout(), 5000),
);
});
});
4 changes: 4 additions & 0 deletions integration-tests/tests/src/setup-globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,7 @@ const { defaultLogLevel = "off" } = environment;
Realm.setLogLevel(defaultLogLevel);

Realm.flags.THROW_ON_GLOBAL_REALM = true;

after(() => {
Realm.shutdown();
});
2 changes: 1 addition & 1 deletion packages/realm/bindgen/vendor/realm-core
Submodule realm-core updated 2 files
+23 −0 CHANGELOG.md
+1 −1 bindgen/spec.yml
1 change: 0 additions & 1 deletion packages/realm/src/ProgressRealmPromise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export class ProgressRealmPromise implements Promise<Realm> {
* @internal
*/
public static cancelAll() {
assert(flags.ALLOW_CLEAR_TEST_STATE, "Set the flags.ALLOW_CLEAR_TEST_STATE = true before calling this.");
for (const promiseRef of ProgressRealmPromise.instances) {
promiseRef.deref()?.cancel();
}
Expand Down
21 changes: 16 additions & 5 deletions packages/realm/src/Realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
flags,
fromBindingRealmSchema,
fs,
garbageCollection,
normalizeObjectSchema,
normalizeRealmSchema,
toArrayBuffer,
Expand Down Expand Up @@ -175,12 +176,10 @@ export class Realm {
}

/**
* Clears the state by closing and deleting any Realm in the default directory and logout all users.
* NOTE: Not a part of the public API and it's primarily used from the library's tests.
* @private
* Closes all Realms, cancels all pending {@link Realm.open} calls, clears internal caches, resets the logger and collects garbage.
* Call this method to free up the event loop and allow Node.js to perform a graceful exit.
*/
public static clearTestState(): void {
assert(flags.ALLOW_CLEAR_TEST_STATE, "Set the flags.ALLOW_CLEAR_TEST_STATE = true before calling this.");
public static shutdown() {
// Close any realms not already closed
for (const realmRef of Realm.internals) {
const realm = realmRef.deref();
Expand All @@ -193,6 +192,18 @@ export class Realm {
binding.App.clearCachedApps();
ProgressRealmPromise.cancelAll();

binding.Logger.setDefaultLogger(null);
garbageCollection.collect();
}

/**
* Clears the state by closing and deleting any Realm in the default directory and logout all users.
* NOTE: Not a part of the public API and it's primarily used from the library's tests.
* @private
*/
public static clearTestState(): void {
assert(flags.ALLOW_CLEAR_TEST_STATE, "Set the flags.ALLOW_CLEAR_TEST_STATE = true before calling this.");
Realm.shutdown();
// Delete all Realm files in the default directory
const defaultDirectoryPath = fs.getDefaultDirectoryPath();
fs.removeRealmFilesFromDirectory(defaultDirectoryPath);
Expand Down
2 changes: 2 additions & 0 deletions packages/realm/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ export { fs } from "./platform/file-system";
export { network } from "./platform/network";
/** @internal */
export { syncProxyConfig } from "./platform/sync-proxy-config";
/** @internal */
export { garbageCollection } from "./platform/garbage-collection";
27 changes: 27 additions & 0 deletions packages/realm/src/platform/garbage-collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

type ShutdownType = { collect: () => void };

export const garbageCollection: ShutdownType = {
collect() {},
};

export function inject(value: ShutdownType) {
Object.freeze(Object.assign(garbageCollection, value));
}
32 changes: 32 additions & 0 deletions packages/realm/src/platform/node/garbage-collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import v8 from "node:v8";
import vm from "node:vm";

import { inject } from "../garbage-collection";

inject({
collect() {
// Ensure we have the gc function available
v8.setFlagsFromString("--expose_gc");
const gc = vm.runInNewContext("gc");
// Garbage collect
process.nextTick(gc);
},
});
1 change: 1 addition & 0 deletions packages/realm/src/platform/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import "./fs";
import "./device-info";
import "./sync-proxy-config";
import "./custom-inspect";
import "./garbage-collection";

import { Realm } from "../../Realm";
export = Realm;

0 comments on commit 30561b0

Please sign in to comment.