Skip to content

Services: Config types, events, stub execution class, and started promise - #461

Merged
aomarks merged 5 commits into
servicefrom
service.2
Oct 17, 2022
Merged

Services: Config types, events, stub execution class, and started promise#461
aomarks merged 5 commits into
servicefrom
service.2

Conversation

@aomarks

@aomarks aomarks commented Oct 15, 2022

Copy link
Copy Markdown
Member
  1. Added a started promise property to ScriptChildProcess. We'll be re-using this class for services, and we'll need to know when a child process has spawned. We didn't need that exposed before, because all that mattered was the exit code.

  2. Added events and logging specific to services.

  3. Refactored the config types so that classes can hold copies of just a subset of the fields of a config, instead of the full object. In watch mode, service child processes will get handed-off across iterations, and potentially across different build graphs, so I wanted to make sure we don't have a memory leak relating to keeping around references to old build graphs (configs hold references to their dependencies, so potentially that keeps the full build graph live).

  4. Added a ServiceScriptConfig which is guaranteed to have service:false, and a stub ServiceScriptExecution which currently can produce a fingerprint, but doesn't yet actually start/stop the service command.

More incremental work towards services.

Comment thread src/script-child-process.ts Outdated
* Resolves when this child process ends.
*/
readonly completed: Promise<
get completed(): Promise<

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this just be:

readonly completed = this._completed.promise;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's even going to be a new run of the child process with new Deferreds for started and completion, then the getters are safer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, done!

If there's even going to be a new run of the child process with new Deferreds for started and completion, then the getters are safer.

The child process will only be re-used if it doesn't need to be restarted. Otherwise there will be a new ScriptChildProcess instance. So this is safe.

Comment thread src/script-child-process.ts Outdated
* Resolves when this child process ends.
*/
readonly completed: Promise<
get completed(): Promise<

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's even going to be a new run of the child process with new Deferreds for started and completion, then the getters are safer.

break;
}
default: {
const never: never = this._state;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be folded into the previous case?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's a different error, and it has a never assignment to prove to TypeScript that it can't happen. Or did I misunderstand somethning?

}
default: {
const never: never = this._state;
const exception = new Error(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want the Error to have a stack trace, then you can throw it inside a try/catch and call reject() after it's caught.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool trick. In this case the error string is unique, so I don't think a trace trace really adds much value.

});

this._child.on('close', (status, signal) => {
if (this._state === 'killing') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to ensure or assert that _started is settled in any of these cases?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. close should only happen after spawn or error.

Comment thread src/analyzer.ts
dependencies: config.dependencies as Array<Dependency<ScriptConfig>>,
// Unfortunately TypeScript doesn't narrow the ...config spread, so we
// have to assign explicitly.
service: config.service,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you cast the config: ...(config as ServiceScriptConfig)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but then we'd be bypassing the type checker all together. Here I'm trying to prove to the type checker (and hence ourselves) that the 3 different configs can be discriminated.

Comment thread src/analyzer.ts
...config,
state: 'valid',
extraArgs: undefined,
dependencies: config.dependencies as Array<Dependency<ScriptConfig>>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this cast needed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because that property requires validated dependencies, but the type checker still believes they could be unvalidated, because that's checked earlier in a way that doesn't cause the type checker to narrow.

@aomarks
aomarks merged commit 525b625 into service Oct 17, 2022
@aomarks
aomarks deleted the service.2 branch October 17, 2022 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants