Skip to content

fix: wait for the document server before configuring its connector; 34.0.3:3 -> 34.0.3:4 - #141

Open
MattDHill wants to merge 1 commit into
masterfrom
fix/office-suite-polish
Open

fix: wait for the document server before configuring its connector; 34.0.3:3 -> 34.0.3:4#141
MattDHill wants to merge 1 commit into
masterfrom
fix/office-suite-polish

Conversation

@MattDHill

Copy link
Copy Markdown
Member

Follow-up to #139 and #140. Fixes a hang found on a production server, and closes the review findings on the office-suite work.

The hang

Selecting a suite and starting Nextcloud before the document server is up leaves editing permanently broken. From the reporting server's log:

[proxy:error] (111)Connection refused: AH00957: http: attempt to connect to 10.0.3.1:50666 failed
"GET /hosting/discovery HTTP/1.1" 503
office-suite: richdocuments:activate-config failed: ✓ Set WOPI url to http://127.0.0.1

richdocuments:activate-config sets the WOPI url and then fetches discovery back through this container's own proxy. Collabora had bound its port but was not yet accepting connections, so the fetch 503'd and left the app holding a url with no discovery behind it. Every document open then posted to /apps/richdocuments/token, got a 500, and showed a spinner that never resolved. Nothing was address-specific, so LAN and clearnet failed alike.

It never recovered because the reconcile logged a warning and returned without recording its signature, which only schedules a retry on the next chain build — and once the backend address has resolved, nothing rebuilds the chain.

The fix

A bridge address is not a readiness signal. The port is bound, and the address therefore resolves, well before coolwsd accepts a connection. The oneshot now waits on the document server's own health check before touching Nextcloud at all:

await sdk
  .getStatus(effects, { packageId })
  .waitFor((s) => s?.health[healthCheckId]?.result === 'success')

That check fetches the endpoint this package depends on, so a pass means the work can succeed rather than merely that something is listening. The wait is a subscription, not a poll: no commands, no failure mode, and it releases the instant the service is ready — whether that is seconds later or whenever the user gets around to installing it.

Past the gate every remaining failure is unexpected, so none of them throw. A oneshot whose fn rejects is re-invoked by the SDK on a widening backoff, which would run occ forever against a service that is simply not there.

Also in this change

  • Switching suites disables the connector it moves away from, and enables the one just selected. richdocuments demotes the Microsoft formats whenever it finds a rival enabled, so every switch previously landed on a failing health check naming a step the package could take itself. occ refuses an app with no release compatible with the running major version, so that protection is enforced without a check of our own.
  • Both office services are declared in the manifest. An uninstalled one now shows its own title and icon instead of a bare package id, and setupDependencies is typed against the manifest so a missing declaration is a compile error.
  • The four Apache proxy modules move to a2enmod in the image. Only the generated proxy config is still written at runtime.
  • Health-check messages are whole sentences with a parameter rather than six concatenated fragments, which is what makes them translatable. The check polls every two minutes once settled, keeping the SDK default's one-second first result and fast recovery.
  • The ONLYOFFICE secret is read after the gate, since its install writes that file after its port is bound and a snapshot taken at chain build can be empty.
  • Docs corrected everywhere they still told users to install the connector app by hand, which the package has done since 34.0.3:3. Same for the sibling service repos.

Verified on a dev box

  • With Collabora stopped and still selected, Nextcloud reaches running with a green Web Interface in under three seconds. The oneshot parks on the watch and logs nothing.
  • Starting Collabora afterwards: its check went green at 01:45:27 and the reconcile fired the same second, with Nextcloud never restarted and its started timestamp unchanged.
  • /hosting/discovery through the proxy returns 200 with every urlsrc rewritten relative.
  • Connector disabled by hand goes red naming the app to enable, and is not put back on restart. Selecting None drops the check; re-selecting Collabora enables the app and returns to green.

Version goes to 34.0.3:4 because :3 reached beta. A switch to ONLYOFFICE is untested, since that package is not on the dev box.

🤖 Generated with Claude Code

…4.0.3:3 -> 34.0.3:4

Selecting a suite and starting Nextcloud before the document server was up left
editing permanently broken. `richdocuments:activate-config` sets the WOPI url and
then fetches discovery through this container's own proxy; when Collabora had
bound its port but was not yet accepting connections the fetch returned 503,
leaving the app holding a url with no discovery behind it. Every document then
posted to the token endpoint, got a 500, and opened to a spinner that never
resolved. The reconcile logged a warning and returned without recording its
signature, which only schedules a retry on the next chain build — and nothing
rebuilds the chain once the backend address has resolved, so it never retried.

A bridge address is not a readiness signal: the port is bound, and the address
therefore resolves, well before coolwsd accepts a connection. The oneshot now
waits on the document server's own health check before it touches Nextcloud at
all. That check fetches the endpoint this package depends on, so a pass means
the work can succeed rather than merely that something is listening. The wait is
a subscription, not a poll: it runs no commands, cannot fail, and releases the
instant the service is ready, whether that is seconds later or whenever the user
installs it. Nothing in the chain requires the oneshot, so a parked one holds
nothing up. Past the gate every failure is unexpected, so none of them throw —
a rejected oneshot fn is re-invoked on a widening backoff, which would run occ
forever against a service that is simply not there.

Switching suites now disables the connector it moves away from. richdocuments
demotes the Microsoft formats whenever it finds a rival enabled, so every switch
previously landed on a failing health check naming a step the package could take
itself. It also enables the connector for the suite just selected, since occ
refuses an app with no release compatible with the running major version and
enforces that protection on its own.

Both office services are declared in the manifest, so an uninstalled one is
shown with its own title and icon rather than a bare package id, and the
dependency result is typed against the manifest so a missing declaration is a
compile error. The four Apache proxy modules move to `a2enmod` in the image;
only the generated proxy config is still written at runtime. Health-check
messages become whole sentences with a parameter rather than six concatenated
fragments, and the check polls sparingly once settled while keeping the SDK
default's fast first result and recovery.

The ONLYOFFICE secret is read after the gate, because its install writes that
file after its port is bound and a snapshot taken at chain build can be empty.
Everywhere the docs still told users to install the connector app by hand is
corrected; the package has done that since 34.0.3:3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant