Repository: solid/community-server
Pull request #1096 (2021-12-13 14:00:49)

add: Add VoidLocker and unittest
#### 📁 Related issues

Closes: https://github.com/solid/community-server/issues/1093#issuecomment-991052456

#### ✍️ Description

This is an implementation of VoidLocker.



Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 70bb6acde..505e14ab9 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -3,6 +3,7 @@
 ## v3.0.0
 ### New features
 - The Identity Provider now uses the `webid` scope as required for Solid-OIDC.
+- The `VoidLocker` can be used to disable locking for development/testing purposes. This can be enabled by changing the `/config/util/resource-locker/` import to `debug-void.json`
 
 ### Configuration changes
 You might need to make changes to your v2 configuration if you use a custom config.
diff --git a/config/util/README.md b/config/util/README.md
index 831e18d23..5dc8dc3c2 100644
--- a/config/util/README.md
+++ b/config/util/README.md
@@ -36,6 +36,7 @@ to the ChainedConverter list.
 
 ## Resource-locker
 Which locking mechanism to use to for example prevent 2 write simultaneous write requests.
+* *debug-void*: No locking mechanism, does not prevent simultaneous read/writes.
 * *memory*: Uses an in-memory locking mechanism.
 * *redis*: Uses a Redis store for locking.
 
diff --git a/config/util/resource-locker/debug-void.json b/config/util/resource-locker/debug-void.json
new file mode 100644
index 000000000..c02f6e129
--- /dev/null
+++ b/config/util/resource-locker/debug-void.json
@@ -0,0 +1,13 @@
+{
+  "@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^2.0.0/components/context.jsonld",
+  "@graph": [
+    {
+      "comment": [
+        "DO NOT USE IN PRODUCTION. ONLY FOR DEVELOPMENT, TESTING, OR DEBUGGING.",
+        "Allows multiple simultaneous read operations and write operations without locks."
+      ],
+      "@id": "urn:solid-server:default:ResourceLocker",
+      "@type": "VoidLocker"
+    }
+  ]
+}
diff --git a/src/index.ts b/src/index.ts
index 8621ee257..6e77d9807 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -363,6 +363,7 @@ export * from './util/locking/RedisResourceLocker';
 export * from './util/locking/ResourceLocker';
 export * from './util/locking/SingleThreadedResourceLocker';
 export * from './util/locking/WrappedExpiringReadWriteLocker';
+export * from './util/locking/VoidLocker';
 
 // Util/Templates
 export * from './util/templates/ChainedTemplateEngine';
diff --git a/src/util/locking/VoidLocker.ts b/src/util/locking/VoidLocker.ts
new file mode 100644
index 000000000..bc79f792e
--- /dev/null
+++ b/src/util/locking/VoidLocker.ts
@@ -0,0 +1,34 @@
+import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
+import { getLoggerFor } from '../../logging/LogUtil';
+import type { ExpiringReadWriteLocker } from './ExpiringReadWriteLocker';
+
+/**
+ * This locker will execute the whileLocked function without any locking mechanism
+ *
+ * Do not use this locker in combination with storages that doesn't handle concurrent read/writes gracefully
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+function noop(): void {}
+
+export class VoidLocker implements ExpiringReadWriteLocker {
+  protected readonly logger = getLoggerFor(this);
+
+  public constructor() {
+    this.logger.warn('Locking mechanism disabled; data integrity during parallel requests not guaranteed.');
+  }
+
+  public async withReadLock<T>(
+    identifier: ResourceIdentifier,
+    whileLocked: (maintainLock: () => void) => T | Promise<T>,
+  ): Promise<T> {
+    return whileLocked(noop);
+  }
+
+  public async withWriteLock<T>(
+    identifier: ResourceIdentifier,
+    whileLocked: (maintainLock: () => void) => T | Promise<T>,
+  ): Promise<T> {
+    return whileLocked(noop);
+  }
+}


Test patch:
diff --git a/test/unit/util/locking/VoidLocker.test.ts b/test/unit/util/locking/VoidLocker.test.ts
new file mode 100644
index 000000000..7bb5c0885
--- /dev/null
+++ b/test/unit/util/locking/VoidLocker.test.ts
@@ -0,0 +1,27 @@
+import type { ResourceIdentifier } from '../../../../src';
+import { VoidLocker } from '../../../../src/util/locking/VoidLocker';
+
+describe('A VoidLocker', (): void => {
+  it('invokes the whileLocked function immediately with readLock.', async(): Promise<void> => {
+    const locker = new VoidLocker();
+    const identifier: ResourceIdentifier = { path: 'http://test.com/res' };
+    const whileLocked = jest.fn().mockImplementation((maintainLockFn: () => void): void => {
+      maintainLockFn();
+    });
+
+    await locker.withReadLock(identifier, whileLocked);
+
+    expect(whileLocked).toHaveBeenCalledTimes(1);
+  });
+
+  it('invokes the whileLocked function immediately with writeLock.', async(): Promise<void> => {
+    const locker = new VoidLocker();
+    const identifier: ResourceIdentifier = { path: 'http://test.com/res' };
+    const whileLocked = jest.fn().mockImplementation((maintainLockFn: () => void): void => {
+      maintainLockFn();
+    });
+    await locker.withWriteLock(identifier, whileLocked);
+
+    expect(whileLocked).toHaveBeenCalledTimes(1);
+  });
+});