Skip to content

Commit

Permalink
feat: initial proposal for multithreaded execution
Browse files Browse the repository at this point in the history
  • Loading branch information
Falx authored and joachimvh committed May 16, 2022
1 parent 32245fc commit 236bbc6
Show file tree
Hide file tree
Showing 40 changed files with 880 additions and 97 deletions.
11 changes: 10 additions & 1 deletion README.md
Expand Up @@ -116,8 +116,17 @@ to some commonly used settings:
| `--sparqlEndpoint, -s` | | URL of the SPARQL endpoint, when using a quadstore-based configuration. |
| `--showStackTrace, -t` | false | Enables detailed logging on error output. |
| `--podConfigJson` | `./pod-config.json` | Path to the file that keeps track of dynamic Pod configurations. Only relevant when using `@css:config/dynamic.json`. |
| `--seededPodConfigJson` | | Path to the file that keeps track of seeded Pod configurations. |
| `--seededPodConfigJson`| | Path to the file that keeps track of seeded Pod configurations. |
| `--mainModulePath, -m` | | Path from where Components.js will start its lookup when initializing configurations. |
| `--workers, -w` | `1` | Run in multithreaded mode using workers. Special values are `-1` (scale to `num_cores-1`), `0` (scale to `num_cores`) and 1 (singlethreaded). |

### 🔀 Multithreading
The Community Solid Server can be started in multithreaded mode with any config. The config must only contain components that are threadsafe though. If a non-threadsafe component is used in multithreaded mode, the server will describe with an error which class is the culprit.

```node
# Running multithreaded with autoscaling to number of logical cores minus 1
npm start -- -c config/file.json -w -1
```

### 🧶 Custom configurations
More substantial changes to server behavior can be achieved
Expand Down
5 changes: 5 additions & 0 deletions RELEASE_NOTES.md
Expand Up @@ -6,6 +6,7 @@
- Components.js was upgraded to v5. If you have created an external component
you should also upgrade to prevent warnings and conflicts.
- A new FileSystemResourceLocker has been added. It allows for true threadsafe locking without external dependencies.
- The CSS can now run multithreaded with multiple workers, this is done with the `--workers` or `-w` flag.

### Data migration
The following actions are required if you are upgrading from a v4 server and want to retain your data.
Expand All @@ -30,13 +31,17 @@ The following changes are relevant for v3 custom configs that replaced certain f
- `/ldp/metadata-parser/default.json`
- `/storage/backend/*-quota-file.json`
- `/storage/backend/quota/quota-file.json`
- The structure of the init configs has changed significantly to support worker threads.
- `/app/init/*`

### Interface changes
These changes are relevant if you wrote custom modules for the server that depend on existing interfaces.
- `YargsCliExtractor` was changed to now take as input an array of parameter objects.
- `RedirectAllHttpHandler` was removed and fully replaced by `RedirectingHttpHandler`.
- `SingleThreadedResourceLocker` has been renamed to `MemoryResourceLocker`.

A new interface `SingleThreaded` has been added. This empty interface can be implemented to mark a component as not-threadsafe. When the CSS starts in multithreaded mode, it will error and halt if any SingleThreaded components are instantiated.

## V4.0.1
Freezes the `oidc-provider` dependency to prevent a potential issue with the solid authn client
as described in https://github.com/inrupt/solid-client-authn-js/issues/2103.
Expand Down
45 changes: 39 additions & 6 deletions config/app/init/base/init.json
Expand Up @@ -5,7 +5,8 @@
"css:config/app/init/initializers/logger.json",
"css:config/app/init/initializers/server.json",
"css:config/app/init/initializers/seeded-pod.json",
"css:config/app/init/initializers/version.json"
"css:config/app/init/initializers/version.json",
"css:config/app/init/initializers/workers.json"
],
"@graph": [
{
Expand All @@ -14,12 +15,44 @@
"@type": "SequenceHandler",
"handlers": [
{ "@id": "urn:solid-server:default:LoggerInitializer" },
{ "@id": "urn:solid-server:default:BaseUrlVerifier" },
{ "@id": "urn:solid-server:default:ParallelInitializer" },
{ "@id": "urn:solid-server:default:SeededPodInitializer" },
{ "@id": "urn:solid-server:default:ServerInitializer" },
{ "@id": "urn:solid-server:default:ModuleVersionVerifier" }
{ "@id": "urn:solid-server:default:PrimaryInitializer" },
{ "@id": "urn:solid-server:default:WorkerInitializer" }
]
},
{
"comment": "This wrapped sequence handler will be executed ONLY BY THE PRIMARY PROCESS when starting the server.",
"@id": "urn:solid-server:default:PrimaryInitializer",
"@type": "ProcessHandler",
"executeOnPrimary": true,
"clusterManager": { "@id": "urn:solid-server:default:ClusterManager" },
"source": {
"comment": "These initializers will all be executed sequentially when starting the server.",
"@id": "urn:solid-server:default:PrimarySequenceInitializer",
"@type":"SequenceHandler",
"handlers": [
{ "@id": "urn:solid-server:default:BaseUrlVerifier" },
{ "@id": "urn:solid-server:default:PrimaryParallelInitializer" },
{ "@id": "urn:solid-server:default:SeededPodInitializer" },
{ "@id": "urn:solid-server:default:ModuleVersionVerifier" },
{ "@id": "urn:solid-server:default:WorkerManager" }
]
}
},
{
"comment": "This wrapped sequence handler will be executed ONLY BY THE WORKER PROCESSES when starting the server.",
"@id": "urn:solid-server:default:WorkerInitializer",
"@type": "ProcessHandler",
"executeOnPrimary": false,
"clusterManager": { "@id": "urn:solid-server:default:ClusterManager" },
"source": {
"comment": "These initializers will all be executed sequentially when starting the server.",
"@id": "urn:solid-server:default:WorkerSequenceInitializer",
"@type": "SequenceHandler",
"handlers": [
{ "@id": "urn:solid-server:default:WorkerParallelInitializer" },
{ "@id": "urn:solid-server:default:ServerInitializer" }
]
}
}
]
}
10 changes: 8 additions & 2 deletions config/app/init/default.json
Expand Up @@ -5,8 +5,14 @@
],
"@graph": [
{
"comment": "These handlers are called whenever the server is started, and can be used to ensure that all necessary resources for booting are available.",
"@id": "urn:solid-server:default:ParallelInitializer",
"comment": "These handlers are called only for the Primary process whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)",
"@id": "urn:solid-server:default:PrimaryParallelInitializer",
"@type": "ParallelHandler",
"handlers": [ ]
},
{
"comment": "These handlers are called only for the workers processes whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)",
"@id": "urn:solid-server:default:WorkerParallelInitializer",
"@type": "ParallelHandler",
"handlers": [ ]
}
Expand Down
10 changes: 8 additions & 2 deletions config/app/init/initialize-prefilled-root.json
Expand Up @@ -6,12 +6,18 @@
],
"@graph": [
{
"comment": "These handlers are called whenever the server is started, and can be used to ensure that all necessary resources for booting are available.",
"@id": "urn:solid-server:default:ParallelInitializer",
"comment": "These handlers are called only for the Primary process whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)",
"@id": "urn:solid-server:default:PrimaryParallelInitializer",
"@type": "ParallelHandler",
"handlers": [
{ "@id": "urn:solid-server:default:RootInitializer" }
]
},
{
"comment": "These handlers are called only for the workers processes whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)",
"@id": "urn:solid-server:default:WorkerParallelInitializer",
"@type": "ParallelHandler",
"handlers": [ ]
}
]
}
10 changes: 8 additions & 2 deletions config/app/init/initialize-root.json
Expand Up @@ -6,12 +6,18 @@
],
"@graph": [
{
"comment": "These handlers are called whenever the server is started, and can be used to ensure that all necessary resources for booting are available.",
"@id": "urn:solid-server:default:ParallelInitializer",
"comment": "These handlers are called only for the Primary process whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)",
"@id": "urn:solid-server:default:PrimaryParallelInitializer",
"@type": "ParallelHandler",
"handlers": [
{ "@id": "urn:solid-server:default:RootInitializer" }
]
},
{
"comment": "These handlers are called only for the workers processes whenever the server is started, and can be used to ensure that all necessary resources for booting are available. (in singlethreaded mode, these are always called)",
"@id": "urn:solid-server:default:WorkerParallelInitializer",
"@type": "ParallelHandler",
"handlers": [ ]
}
]
}
11 changes: 11 additions & 0 deletions config/app/init/initializers/workers.json
@@ -0,0 +1,11 @@
{
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^5.0.0/components/context.jsonld",
"@graph": [
{
"comment": "Spawns the required amount of workers",
"@id": "urn:solid-server:default:WorkerManager",
"@type": "WorkerManager",
"clusterManager": { "@id": "urn:solid-server:default:ClusterManager" }
}
]
}
7 changes: 6 additions & 1 deletion config/app/main/default.json
Expand Up @@ -13,7 +13,12 @@
"finalizers": [
{ "@id": "urn:solid-server:default:ServerInitializer" }
]
}
},
"clusterManager": {
"@id": "urn:solid-server:default:ClusterManager",
"@type": "ClusterManager",
"workers": { "@id": "urn:solid-server:default:variable:workers" }
}
}
]
}
10 changes: 10 additions & 0 deletions config/app/variables/cli/cli.json
Expand Up @@ -102,6 +102,16 @@
"type": "string",
"describe": "Path to the file that will be used to seed pods."
}
},
{
"@type": "YargsParameter",
"name": "workers",
"options": {
"alias": "w",
"requiresArg": true,
"type": "number",
"describe": "Run the server in multithreaded mode using workers. (special values: -1: num_cores-1, 0: num_cores). Defaults to 1 (singlethreaded)"
}
}
],
"options": {
Expand Down
8 changes: 8 additions & 0 deletions config/app/variables/resolver/resolver.json
Expand Up @@ -65,6 +65,14 @@
"@type": "AssetPathExtractor",
"key": "seededPodConfigJson"
}
},
{
"CombinedSettingsResolver:_resolvers_key": "urn:solid-server:default:variable:workers",
"CombinedSettingsResolver:_resolvers_value": {
"@type": "KeyExtractor",
"key": "workers",
"defaultValue": 1
}
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion config/identity/access/restricted.json
Expand Up @@ -13,7 +13,7 @@
},
{
"comment": "IDP-related containers require initialized resources to support authorization.",
"@id": "urn:solid-server:default:ParallelInitializer",
"@id": "urn:solid-server:default:PrimaryParallelInitializer",
"@type": "ParallelHandler",
"handlers": [
{ "@id": "urn:solid-server:default:IdpContainerInitializer" },
Expand Down
4 changes: 2 additions & 2 deletions config/identity/pod/dynamic.json
Expand Up @@ -23,8 +23,8 @@
},

{
"comment": "Add to the list of initializers.",
"@id": "urn:solid-server:default:ParallelInitializer",
"comment": "Add to the list of primary initializers.",
"@id": "urn:solid-server:default:PrimaryParallelInitializer",
"@type": "ParallelHandler",
"handlers": [
{
Expand Down
5 changes: 5 additions & 0 deletions config/util/variables/default.json
Expand Up @@ -41,6 +41,11 @@
"comment": "Path to the JSON file used to seed pods.",
"@id": "urn:solid-server:default:variable:seededPodConfigJson",
"@type": "Variable"
},
{
"comment": "Run the server in multithreaded mode with the set amount of workers.",
"@id": "urn:solid-server:default:variable:workers",
"@type": "Variable"
}
]
}
17 changes: 10 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -102,7 +102,7 @@
"arrayify-stream": "^2.0.0",
"async-lock": "^1.3.0",
"bcrypt": "^5.0.1",
"componentsjs": "^5.0.1",
"componentsjs": "^5.1.0",
"cors": "^2.8.5",
"cross-fetch": "^3.1.5",
"ejs": "^3.1.6",
Expand All @@ -113,6 +113,7 @@
"handlebars": "^4.7.7",
"ioredis": "^5.0.4",
"jose": "^4.4.0",
"jsonld-context-parser": "^2.1.5",
"lodash.orderby": "^4.6.0",
"marked": "^4.0.12",
"mime-types": "^2.1.34",
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Expand Up @@ -187,6 +187,11 @@ export * from './identity/storage/WebIdAdapterFactory';
export * from './identity/IdentityProviderHttpHandler';
export * from './identity/OidcHttpHandler';

// Init/Cluster
export * from './init/cluster/ClusterManager';
export * from './init/cluster/SingleThreaded';
export * from './init/cluster/WorkerManager';

// Init/Final
export * from './init/final/Finalizable';
export * from './init/final/ParallelFinalizer';
Expand Down Expand Up @@ -218,9 +223,9 @@ export * from './init/ConfigPodInitializer';
export * from './init/ContainerInitializer';
export * from './init/Initializer';
export * from './init/LoggerInitializer';
export * from './init/ModuleVersionVerifier';
export * from './init/SeededPodInitializer';
export * from './init/ServerInitializer';
export * from './init/ModuleVersionVerifier';

// Logging
export * from './logging/LazyLoggerFactory';
Expand Down Expand Up @@ -401,6 +406,7 @@ export * from './util/handlers/ConditionalHandler';
export * from './util/handlers/HandlerUtil';
export * from './util/handlers/MethodFilterHandler';
export * from './util/handlers/ParallelHandler';
export * from './util/handlers/ProcessHandler';
export * from './util/handlers/SequenceHandler';
export * from './util/handlers/StaticHandler';
export * from './util/handlers/StaticThrowHandler';
Expand Down
5 changes: 4 additions & 1 deletion src/init/App.ts
@@ -1,3 +1,4 @@
import type { ClusterManager } from './cluster/ClusterManager';
import type { Finalizable } from './final/Finalizable';
import type { Initializer } from './Initializer';

Expand All @@ -7,10 +8,12 @@ import type { Initializer } from './Initializer';
export class App {
private readonly initializer: Initializer;
private readonly finalizer: Finalizable;
public readonly clusterManager: ClusterManager;

public constructor(initializer: Initializer, finalizer: Finalizable) {
public constructor(initializer: Initializer, finalizer: Finalizable, clusterManager: ClusterManager) {
this.initializer = initializer;
this.finalizer = finalizer;
this.clusterManager = clusterManager;
}

/**
Expand Down

0 comments on commit 236bbc6

Please sign in to comment.