Skip to content

Add site setting to configure server-side script execution timeout#7862

Open
labkey-martyp wants to merge 3 commits into
release26.7-SNAPSHOTfrom
26.7_fb_script_timeout_setting
Open

Add site setting to configure server-side script execution timeout#7862
labkey-martyp wants to merge 3 commits into
release26.7-SNAPSHOTfrom
26.7_fb_script_timeout_setting

Conversation

@labkey-martyp

@labkey-martyp labkey-martyp commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Rationale

The Rhino sandbox terminates any server-side JavaScript (such as trigger scripts) after a hard-coded 60 seconds of wall-clock time, which includes database and other Java operations invoked by the script. Long-running but legitimate operations, such as bulk imports that fire per-row triggers, can exceed this budget with no recourse. This change makes the timeout a configurable site setting so admins can raise it, or disable it entirely, without a code change.

Related Pull Requests

None.

Changes

  • New scriptExecutionTimeout site setting (default 60 seconds, 0 disables) exposed on the Site Settings admin page and as a SiteSettings.scriptExecutionTimeout startup property.
  • The Rhino sandbox resolves the timeout once per script execution instead of using the hard-coded constant, so setting changes take effect immediately for new script runs; the elapsed-time comparison is promoted to long to avoid overflow with large values.
  • The Site Settings form binds the new field as a nullable Integer, so a POST that omits the parameter leaves the stored setting unchanged rather than silently disabling the timeout.
  • Added RhinoService.TestCase.timeoutTest (BVT) covering both a short timeout aborting a runaway script and 0 disabling the watchdog.

Replace the hardcoded 60-second Rhino sandbox timeout with a site setting on the Admin Console Site Settings page. The limit is wall-clock time measured from script context creation, so it includes time spent in database operations and nested trigger scripts fired from the script; bulk imports that fire large trigger chains can exceed 60 seconds even when the scripts themselves do little work.

The default remains 60 seconds and 0 disables the timeout, following the convention of the adjacent readOnlyHttpRequestTimeout setting. The value is resolved once per Rhino context since observeInstructionCount runs every 30k instructions, and it is also settable as a startup property via SiteSettings.scriptExecutionTimeout.

Claude-Session: https://claude.ai/code/session_01CvVMhiSzPnaJtdn1w2Mef4
Follow-up to the scriptExecutionTimeout site setting: the SiteSettingsForm field is now a nullable Integer so a POST that omits the parameter leaves the stored setting unchanged instead of binding 0 and silently disabling the script watchdog. Saving is skipped when the value is absent, and the negative-value validation is null-guarded.

Also adds RhinoService.TestCase.timeoutTest (BVT), which verifies that a 1-second timeout aborts a runaway script with the expected error message and that a value of 0 disables the watchdog entirely.

Claude-Session: https://claude.ai/code/session_01LEjXWPFro12ZV6YcwtFMvi

@labkey-jeckels labkey-jeckels left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

See questions about null handling but no need for additional review unless you want another pass.

private int _sslPort;
private int _memoryUsageDumpInterval;
private int _readOnlyHttpRequestTimeout;
private Integer _scriptExecutionTimeout;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why Integer vs int for other properties?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is set up where 0 means the timeout is disabled, so if the call submitted without the parameter an int would default to 0 and disable it. This makes disabling the timeout a more explicit change in the UI.

props.setSSLPort(form.getSslPort());
props.setMemoryUsageDumpInterval(form.getMemoryUsageDumpInterval());
props.setReadOnlyHttpRequestTimeout(form.getReadOnlyHttpRequestTimeout());
if (form.getScriptExecutionTimeout() != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the admin blanks the field, we don't save the value? Other fields don't work this way. Is there a reason to have a different convention?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah it's the same reason above, that 0 disables the timeout. So blanking it basically just leaves the value as is. You have to specifically set it to 0 if you want no timeout.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That behavior makes more sense to me for an API that has optional properties than for a form-based UI. Consider rejecting the blank value instead of silently ignoring it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point — changed in b8282e0. A blank (or omitted) value is now rejected in validateCommand with "Script execution timeout is required; set to 0 to disable the timeout" instead of being silently ignored. The Integer binding stays, since null is what lets validation distinguish a blank from an explicit 0.

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