Skip to content

Sidecar server#407

Merged
danvk merged 13 commits into
mainfrom
sidecar-server
Aug 24, 2023
Merged

Sidecar server#407
danvk merged 13 commits into
mainfrom
sidecar-server

Conversation

@danvk

@danvk danvk commented Aug 23, 2023

Copy link
Copy Markdown
Collaborator

See #406

This PR adds a serve subcommand to the Python sidecar that runs a web server. Eventually this should be the only command, but for now it coexists with existing sidecar commands. This will let us share much more code between the web and desktop versions of RefStudio.

When the app starts, it uses Command.spawn to run the serve command, starting a web server. This server's port is in the allowlist in tauri.conf.json which allows us to connect to it using Tauri's version of fetch.

A few notes:

  • The server's stderr/stdout are forwarded to the dev console so you can see them, but we could consider putting them in a file (either using the Tauri FS API or something in Pythonland) if this is too noisy.
  • When you quit the app, Tauri closes the server process.
  • When you run in debug mode and make changes to the TypeScript code, Tauri does not kill the server. This means that when we try to start a new server from TypeScript, it will fail due to a port collision. We may want to think more about this, but for now, I'm detecting this situation and killing the old server via a /api/meta/shutdown endpoint.
  • uvicorn is pretty good about keeping the server running when Python throws an exception. But if the process dies, we'll restart it from TypeScript.

Forwarded logs from the server:

image

Killing the server triggers a restart, after which the API is still usable:

kill-and-restart.mov

@codecov

codecov Bot commented Aug 23, 2023

Copy link
Copy Markdown

Codecov Report

Merging #407 (c430308) into main (cb22f73) will decrease coverage by 0.21%.
The diff coverage is 67.83%.

@@            Coverage Diff             @@
##             main     #407      +/-   ##
==========================================
- Coverage   83.18%   82.98%   -0.21%     
==========================================
  Files         183      184       +1     
  Lines       10648    10787     +139     
  Branches     1128     1144      +16     
==========================================
+ Hits         8858     8952      +94     
- Misses       1775     1820      +45     
  Partials       15       15              
Files Changed Coverage Δ
python/main.py 0.00% <0.00%> (ø)
python/sidecar/http.py 82.70% <60.00%> (-3.13%) ⬇️
src/api/server.ts 69.29% <69.29%> (ø)
python/web.py 90.90% <75.00%> (-9.10%) ⬇️
python/sidecar/typing.py 97.56% <100.00%> (+0.03%) ⬆️
src/AppStartup.tsx 96.49% <100.00%> (+0.19%) ⬆️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Comment thread src/api/sidecar.ts Outdated
@danvk danvk changed the title 🚧 Sidecar server 🚧 Sidecar server Aug 24, 2023

@danvk danvk left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Let me know your thoughts about testing. It's important code, but the test is going to involve enough mocks that I'm not sure it will really be that helpful.

Comment thread src/api/server.ts
* If an existing server not managed by us is already running, it will be killed. (This can happen
* when you run Tauri Desktop in dev mode and make TypeScript changes.)
*/
export function runRefStudioServerOnDesktop() {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

There's a possible race condition where we start the server and try to fetch something from it (maybe settings) before it's ready to respond. I'm hoping this won't come up in practice. If it is, we'll want to expose something like function waitForServer(): Promise<void> and await that before running tauriFetch. But this will introduce another difference between desktop and web (where you can assume the server is always running) so I'd really like to avoid that if we can.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's ok. AppStartup (and the splashscreen) is there to ensure we don't call any other API (or render the app) before everything is initialised.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I think we're going to need some kind of waiting logic. Here's what I see in the debug logs. Note that we make at least 3 API calls (init settings, ingest references, read file tree) before the HTTP server reports that it's listening:
image

Comment thread src/api/server.ts
type ServerState = StartingState | ReadyState;

// A null value means that no one has requested a server yet.
let serverProcess: ServerState | null = null;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Lots of ceremony here but I really want to make sure that multiple calls to getOrStartServer don't result in multiple servers being started.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's ok.

@danvk danvk marked this pull request as ready for review August 24, 2023 16:23
@cguedes cguedes self-requested a review August 24, 2023 16:40
cguedes
cguedes previously approved these changes Aug 24, 2023

@cguedes cguedes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just left a comment about a typing definition.

I agree that making (unit) tests for this would be more complex than the value we get. This is something we can test quickly as we build e2e tests for the app!

Comment thread python/sidecar/typing.py Outdated
Comment thread src/api/server.ts
type ServerState = StartingState | ReadyState;

// A null value means that no one has requested a server yet.
let serverProcess: ServerState | null = null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It's ok.

@danvk danvk merged commit 7e79fa0 into main Aug 24, 2023
@danvk danvk deleted the sidecar-server branch August 24, 2023 17:05
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.

2 participants