Skip to content

Commit 24b2ab9

Browse files
authored
## Optimize validateParentPath for Faster Parent Path Validation (#1396)
This commit refactors the `validateParentPath` function in `transaction-shim.ts` to improve its performance, especially for deep or frequent path validations during storage transactions. ### Optimization Details - **Single-pass Traversal:** The previous implementation redundantly checked object-ness and existence at each path level, sometimes re-traversing the same objects. The new approach streamlines the traversal by iterating through the parent path only once, directly walking down the value tree and stopping at the first missing or non-object parent. - **Efficient Error Handling:** Error construction is now more precise, and the function avoids unnecessary object checks and array slicing. The logic for setting the error's `.path` property is simplified, reducing overhead in error cases. - **No Change in Behavior:** All existing functionality and error semantics are preserved. The function still returns the same errors for invalid paths, but does so with less computational overhead.
1 parent 11ef7ff commit 24b2ab9

File tree

1 file changed

+25
-23
lines changed

1 file changed

+25
-23
lines changed

packages/runner/src/storage/transaction-shim.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ function validateParentPath(
5151
value: any,
5252
path: readonly MemoryAddressPathComponent[],
5353
): INotFoundError | null {
54-
if (path.length === 0) {
54+
const pathLength = path.length;
55+
56+
if (pathLength === 0) {
5557
return null; // Root write, no validation needed
5658
}
5759

58-
// Check if the document itself exists and is a record for first-level writes
59-
if (path.length === 1) {
60+
// Check if the document itself exists and is an object for first-level writes
61+
if (pathLength === 1) {
6062
if (value === undefined || !isRecord(value)) {
6163
const pathError: INotFoundError = new Error(
6264
`Cannot access path [${String(path[0])}] - document is not a record`,
@@ -67,33 +69,31 @@ function validateParentPath(
6769
return null;
6870
}
6971

70-
// For deeper paths, check that the parent path exists and is a record
71-
const parentPath = path.slice(0, -1);
72+
// For deeper paths, check that the parent path exists and is an object
73+
const lastIndex = pathLength - 1;
74+
let parentValue = value;
7275

73-
const parentValue = getValueAtPath(value, parentPath);
76+
let parentIndex = 0;
77+
for (parentIndex = 0; parentIndex < lastIndex; parentIndex++) {
78+
if (!isRecord(parentValue)) {
79+
parentValue = undefined;
80+
break;
81+
}
82+
parentValue = parentValue[path[parentIndex] as keyof typeof parentValue];
83+
}
7484

7585
if (
7686
value === undefined || parentValue === undefined || !isRecord(parentValue)
7787
) {
7888
const pathError: INotFoundError = new Error(
79-
`Cannot access path [${
80-
path.map((p) => String(p)).join(", ")
81-
}] - parent path [${
82-
parentPath.map((p) => String(p)).join(", ")
89+
`Cannot access path [${path.join(", ")}] - parent path [${
90+
path.slice(0, lastIndex).join(", ")
8391
}] does not exist or is not a record`,
8492
) as INotFoundError;
8593
pathError.name = "NotFoundError";
8694

8795
// Set pathError.path to last valid parent path component
88-
if (isRecord(value)) {
89-
pathError.path = [];
90-
while (parentPath.length > 0) {
91-
const segment = parentPath.shift()!;
92-
value = value[segment];
93-
if (!isRecord(value)) break;
94-
pathError.path.push(segment);
95-
}
96-
}
96+
if (parentIndex > 0) pathError.path = path.slice(0, parentIndex - 1);
9797

9898
return pathError;
9999
}
@@ -567,10 +567,10 @@ export class ExtendedStorageTransaction implements IExtendedStorageTransaction {
567567
if (writeResult.error && writeResult.error.name === "NotFoundError") {
568568
// Create parent entries if needed
569569
const lastValidPath = writeResult.error.path;
570-
const valueObj = (lastValidPath
570+
const valueObj = lastValidPath
571571
? this.readValueOrThrow({ ...address, path: lastValidPath })
572-
: {}) as Record<string, any>;
573-
if (!isObject(valueObj)) {
572+
: {};
573+
if (!isRecord(valueObj)) {
574574
throw new Error(
575575
`Value at path ${address.path.join("/")} is not an object`,
576576
);
@@ -584,7 +584,9 @@ export class ExtendedStorageTransaction implements IExtendedStorageTransaction {
584584
const lastKey = remainingPath.pop()!;
585585
let nextValue = valueObj;
586586
for (const key of remainingPath) {
587-
nextValue = nextValue[key] = typeof Number(key) === "number" ? [] : {};
587+
nextValue =
588+
nextValue[key] =
589+
(typeof Number(key) === "number" ? [] : {}) as typeof nextValue;
588590
}
589591
nextValue[lastKey] = value;
590592
const parentAddress = { ...address, path: lastValidPath ?? [] };

0 commit comments

Comments
 (0)