Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The rules for this file:
- Replaced connect error alert with dialog (PR #85)
- Added refresh frequency config option for 3D view (PR #86)
- Added more client side widget input validations (PR #87)
- Added configurable UI and kernel timeouts (PR #90)

### Fixed

Expand Down
24 changes: 16 additions & 8 deletions mdadash/backend/kernel/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ async def send_message(self, msg_type: str, data: dict) -> None:
self.kc.shell_channel.send(data_msg)

async def send_message_await_response(
self, msg_type: str, data: dict | None = None, timeout: int = 5
self, msg_type: str, data: dict | None = None, timeout: int | None = None
) -> dict | None:
"""Send message to kernel and wait for a response (async)

Expand All @@ -288,11 +288,12 @@ async def send_message_await_response(
msg_type: str
A message type string that the kernel has a handler registered for

data: dict
data: dict | None
Dict that gets passed to the handler in the kernel (default: None)

timeout: int
Timeout in seconds (default: 5)
timeout: int | None
Timeout in seconds (default: None)
When None, the configured kernel timeout in dashboard configurtion is used.

Returns
-------
Expand All @@ -318,30 +319,35 @@ async def send_message_await_response(
future = asyncio.get_running_loop().create_future()
self._pending_futures[msg_id] = future
self.kc.shell_channel.send(data_msg)
if timeout is None:
timeout = self.sm.dashboard_config["kernel_timeout"]
try:
return await asyncio.wait_for(future, timeout=timeout)
except TimeoutError as e: # pragma: no cover
raise TimeoutError("Timed out waiting for kernel response") from e
finally:
self._pending_futures.pop(msg_id, None)

async def execute_code(self, code: str, timeout: int = 5) -> str:
async def execute_code(self, code: str, timeout: int | None = None) -> str:
"""Execute code in the kernel

Parameters
----------
code: str
Code to execute in the kernel

timeout: int
Timeout in seconds (default: 5)
timeout: int | None
Timeout in seconds (default: None)
When None, the configured kernel timeout in dashboard configurtion is used.

Returns
-------
response: str
A string representation of the output of the code executed

"""
if timeout is None:
timeout = self.sm.dashboard_config["kernel_timeout"]
response = await self.send_message_await_response(
"execute_code", {"code": code}, timeout
)
Expand All @@ -363,10 +369,12 @@ async def connect_to_simulations(self) -> dict:

"""
try:
# Use a larger, but configurable timeout to allow creation of Universes
# Eg: guess_bonds=True could take a while to complete
response = await self.send_message_await_response(
"connect_to_simulations",
self.sm.universe_configs,
timeout=20,
timeout=(6 * self.sm.dashboard_config["kernel_timeout"]),
)
if response["status"] == "ok":
self.sm.running_state["connected"] = True
Expand Down
9 changes: 6 additions & 3 deletions mdadash/backend/state/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def load(self):
"nojump": False,
"socket_bufsize": None,
"buffer_size": 10000000,
"timeout": 5,
"timeout": 600,
"continue_after_disconnect": None,
"step": 1,
"total_steps": None,
Expand Down Expand Up @@ -168,8 +168,11 @@ def _upgrade(self):
"selection_error": "Please enter a selection phrase",
}
settings = self._state["settings"]
if "view3d_frequency" not in settings["dashboard_config"]:
settings["dashboard_config"]["view3d_frequency"] = 1
dashboard_config = settings["dashboard_config"]
if "view3d_frequency" not in dashboard_config:
dashboard_config["view3d_frequency"] = 1
if "kernel_timeout" not in dashboard_config:
dashboard_config["kernel_timeout"] = 5

@property
def state(self) -> dict:
Expand Down
18 changes: 15 additions & 3 deletions mdadash/frontend/src/__tests__/views/SettingsView.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const settings = ref({
dashboard_config: {
show_session_info: true,
show_energies: true,
ui_request_timeout: 5,
kernel_timeout: 5,
n_jobs: 2,
view3d_frequency: 1,
},
Expand Down Expand Up @@ -75,14 +77,24 @@ describe('SettingsView.vue', () => {
await input.setValue(dataMap[name])
}
}
// update n_jobs
// update ui_request_timeout
const inputs = form.findAllComponents({ name: 'VNumberInput' })
const n_jobs = inputs[0]
const ui_request_timeout = inputs[0]
expect(ui_request_timeout).toBeDefined()
expect(ui_request_timeout.props('rules')[0](null)).toMatch(/^Cannot be empty/)
expect(ui_request_timeout.props('rules')[0](5)).toBe(true)
// update kernel_timeout
const kernel_timeout = inputs[1]
expect(kernel_timeout).toBeDefined()
expect(kernel_timeout.props('rules')[0](null)).toMatch(/^Cannot be empty/)
expect(kernel_timeout.props('rules')[0](5)).toBe(true)
// update n_jobs
const n_jobs = inputs[2]
expect(n_jobs).toBeDefined()
expect(n_jobs.props('rules')[0](null)).toMatch(/^Cannot be empty/)
expect(n_jobs.props('rules')[0](2)).toBe(true)
// update view3d_frequency
const view3d_frequency = inputs[1]
const view3d_frequency = inputs[3]
expect(view3d_frequency).toBeDefined()
expect(view3d_frequency.props('rules')[0](null)).toMatch(/^Cannot be empty/)
expect(view3d_frequency.props('rules')[0](1)).toBe(true)
Expand Down
48 changes: 38 additions & 10 deletions mdadash/frontend/src/views/SettingsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,41 @@
</template>
</v-switch>

<v-number-input
class="mb-4"
label="UI timeout"
variant="outlined"
v-model="settings.dashboard_config.ui_request_timeout"
control-variant="default"
hint="Timeout for UI requests in seconds [5]"
:min="5"
:step="1"
persistent-hint
:rules="[(v) => !!v || 'Cannot be empty. Default value is 5']"
validate-on="eager"
></v-number-input>

<v-number-input
class="mb-4"
label="Kernel timeout"
variant="outlined"
v-model="settings.dashboard_config.kernel_timeout"
control-variant="default"
hint="Timeout for Kernel requests in seconds [5]"
:min="5"
:step="1"
persistent-hint
:rules="[(v) => !!v || 'Cannot be empty. Default value is 5']"
validate-on="eager"
></v-number-input>

<v-number-input
class="mb-4"
label="Parallel jobs"
variant="outlined"
v-model="settings.dashboard_config.n_jobs"
control-variant="default"
hint="Number of parallel jobs ('n_jobs' param of joblib.Parallel call)"
hint="Number of parallel jobs ('n_jobs' param of joblib.Parallel call) [2]"
:min="1"
:step="1"
persistent-hint
Expand All @@ -65,7 +93,7 @@
variant="outlined"
v-model="settings.dashboard_config.view3d_frequency"
control-variant="default"
hint="The frequency with which the 3D View is refreshed (every n frames)"
hint="Frequency with which 3D View is refreshed (every n frames) [1]"
:min="1"
:step="1"
persistent-hint
Expand All @@ -83,7 +111,7 @@
<v-form :disabled="runningState.connected">
<v-card-item
title="Universe Configuration"
subtitle="Configuration settings for the universe"
subtitle="Configuration settings for the Universe"
class="cursor-pointer"
@click="isUCExpanded = !isUCExpanded"
>
Expand Down Expand Up @@ -142,7 +170,7 @@
:min="8192"
:max="262144"
:step="1024"
hint="Size of the socket buffer in bytes. Default is to use the system default"
hint="Size of imdclient socket buffer in bytes [System default]"
persistent-hint
></v-number-input>

Expand All @@ -153,7 +181,7 @@
v-model="settings.universe_configs[0].buffer_size"
control-variant="hidden"
:min="1000000"
hint="IMDFrameBuffer will be filled with as many IMDFrame fit in buffer_size bytes [10MB]"
hint="Size of imdclient frame buffer in bytes [1000000]"
persistent-hint
:rules="[(v) => !!v || 'Cannot be empty. Default value is 10000000']"
validate-on="eager"
Expand All @@ -165,12 +193,12 @@
variant="outlined"
v-model="settings.universe_configs[0].timeout"
control-variant="default"
hint="Timeout for the socket in seconds [5]"
hint="Timeout for imdclient socket in seconds [600]"
persistent-hint
:min="0"
:max="600"
:max="900"
:step="1"
:rules="[(v) => !!v || 'Cannot be empty. Default value is 5']"
:rules="[(v) => !!v || 'Cannot be empty. Default value is 600']"
validate-on="eager"
></v-number-input>

Expand All @@ -183,7 +211,7 @@
{ title: 'False', value: false },
{ title: 'None', value: null },
]"
hint="Continue simulation after disconnect"
hint="Continue simulation after disconnect [None]"
persistent-hint
></v-select>

Expand All @@ -194,7 +222,7 @@
v-model="settings.universe_configs[0].step"
:min="1"
control-variant="default"
hint="frame(s) during iteration"
hint="Step n frame(s) during iteration [1]"
persistent-hint
:rules="[(v) => !!v || 'Cannot be empty. Default value is 1']"
validate-on="eager"
Expand Down
Loading