Skip to content

Commit

Permalink
Catch failure to load old schedules in database (#859)
Browse files Browse the repository at this point in the history
* Catch failure to load old schedules in database

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* Revert naming of database name

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* Allow stub to be persistent if we choose to re-use databases

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* Better logs when schedules are ignored or loaded when read from the database, this happens when the server restarts

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* Log jobs left in schedule

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* New schedule app refreshing event, since an ongoing task is force refresh the schedule as well

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* Fix id access for scheduled task

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

* Lint

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>

---------

Signed-off-by: Aaron Chong <aaronchongth@gmail.com>
  • Loading branch information
aaronchongth authored Dec 11, 2023
1 parent b6034a1 commit 286b741
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 20 deletions.
16 changes: 11 additions & 5 deletions packages/api-server/api_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,19 @@ def on_signal(sig, frame):
for t in scheduled_tasks:
user = await User.load_from_db(t.created_by)
if user is None:
logger.warning(f"user [{t.created_by}] does not exist")
logger.warning(f"User who scheduled task, [{t.created_by}] does not exist")
logger.warning(f"Skipping request: [{t.task_request}]")
continue
task_repo = TaskRepository(user)
await routes.scheduled_tasks.schedule_task(t, task_repo)
scheduled += 1
logger.info(f"loaded {scheduled} tasks")
logger.info("successfully started scheduler")
try:
await routes.scheduled_tasks.schedule_task(t, task_repo)
scheduled += 1
except Exception as e: # pylint: disable=broad-except
logger.warning(f"Unable to schedule task requested by {t.created_by}: {e}")
logger.warning(f"Skipping request: [{t.task_request}]")
logger.info(
f"Retrieved {len(scheduled_tasks)} scheduled tasks, scheduled {scheduled} tasks"
)

ros.spin_background()
logger.info("started app")
Expand Down
15 changes: 12 additions & 3 deletions packages/api-server/api_server/authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,22 @@ async def dep(

class StubAuthenticator(JwtAuthenticator):
def __init__(self): # pylint: disable=super-init-not-called
self._user = User(username="stub", is_admin=True)
self._user = None

async def verify_token(self, token: Optional[str]) -> User:
async def _get_user(self, claims: dict) -> User:
if self._user is None:
self._user = await User.load_or_create_from_db("stub")
await self._user.update_admin(True)
return self._user

async def verify_token(self, token: Optional[str]) -> User:
return await self._get_user({})

def fastapi_dep(self) -> Callable[..., Union[Coroutine[Any, Any, User], User]]:
return lambda: self._user
async def dep():
return await self._get_user({})

return dep


if app_config.jwt_public_key:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def do():
if datetime_to_iso[:10] in task.except_dates:
return
asyncio.get_event_loop().create_task(run())
logger.warning(f"schedule has {len(schedule.get_jobs())} jobs left")

for _, j in jobs:
j.do(do)
Expand Down Expand Up @@ -150,7 +151,10 @@ async def del_scheduled_tasks_event(
for sche in task.schedules:
schedule.clear(sche.get_id())

await schedule_task(task, task_repo)
try:
await schedule_task(task, task_repo)
except schedule.ScheduleError as e:
raise HTTPException(422, str(e)) from e


@router.post(
Expand Down
4 changes: 3 additions & 1 deletion packages/api-server/psql_local_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os

from sqlite_local_config import config

here = dirname(__file__)
here = os.path.dirname(__file__)
run_dir = f"{here}/run"

config.update(
Expand Down
1 change: 1 addition & 0 deletions packages/dashboard/src/components/app-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const AppEvents = {
robotSelect: new Subject<[fleetName: string, robotName: string] | null>(),
taskSelect: new Subject<TaskState | null>(),
refreshTaskApp: new Subject<void>(),
refreshTaskSchedule: new Subject<void>(),
refreshAlert: new Subject<void>(),
alertListOpenedAlert: new Subject<Alert | null>(),
disabledLayers: new ReplaySubject<Record<string, boolean>>(),
Expand Down
20 changes: 10 additions & 10 deletions packages/dashboard/src/components/tasks/task-schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const TaskSchedule = () => {
useCreateTaskFormData(rmf);
const username = useGetUsername(rmf);
const [eventScope, setEventScope] = React.useState<string>(EventScopes.CURRENT);
const [refreshTaskAppCount, setRefreshTaskAppCount] = React.useState(0);
const [refreshTaskScheduleCount, setRefreshTaskScheduleCount] = React.useState(0);
const exceptDateRef = React.useRef<Date>(new Date());
const currentEventIdRef = React.useRef<number>(-1);
const [currentScheduleTask, setCurrentScheduledTask] = React.useState<ScheduledTask | undefined>(
Expand All @@ -92,9 +92,9 @@ export const TaskSchedule = () => {
});

React.useEffect(() => {
const sub = AppEvents.refreshTaskApp.subscribe({
const sub = AppEvents.refreshTaskSchedule.subscribe({
next: () => {
setRefreshTaskAppCount((oldValue) => ++oldValue);
setRefreshTaskScheduleCount((oldValue) => ++oldValue);
},
});
return () => sub.unsubscribe();
Expand Down Expand Up @@ -144,7 +144,7 @@ export const TaskSchedule = () => {
onClose={() => {
scheduler.close();
setEventScope(EventScopes.CURRENT);
AppEvents.refreshTaskApp.next();
AppEvents.refreshTaskSchedule.next();
}}
onSubmit={() => {
setOpenCreateTaskForm(true);
Expand All @@ -157,7 +157,7 @@ export const TaskSchedule = () => {
if (eventScope === EventScopes.CURRENT) {
setScheduleToEdit(scheduleWithSelectedDay(task.schedules, exceptDateRef.current));
}
AppEvents.refreshTaskApp.next();
AppEvents.refreshTaskSchedule.next();
scheduler.close();
}}
>
Expand Down Expand Up @@ -200,7 +200,7 @@ export const TaskSchedule = () => {
);

setEventScope(EventScopes.CURRENT);
AppEvents.refreshTaskApp.next();
AppEvents.refreshTaskSchedule.next();
},
[rmf, currentScheduleTask, eventScope],
);
Expand All @@ -225,7 +225,7 @@ export const TaskSchedule = () => {
} else {
await rmf.tasksApi.delScheduledTasksScheduledTasksTaskIdDelete(task.id);
}
AppEvents.refreshTaskApp.next();
AppEvents.refreshTaskSchedule.next();

// Set the default values
setOpenDeleteScheduleDialog(false);
Expand Down Expand Up @@ -265,7 +265,7 @@ export const TaskSchedule = () => {
<div style={{ height: '100%', width: '100%' }}>
<Scheduler
// react-scheduler does not support refreshing, workaround by mounting a new instance.
key={`scheduler-${refreshTaskAppCount}`}
key={`scheduler-${refreshTaskScheduleCount}`}
view="week"
day={defaultDaySettings}
week={defaultWeekSettings}
Expand All @@ -287,7 +287,7 @@ export const TaskSchedule = () => {
value={eventScope}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setEventScope(event.target.value);
AppEvents.refreshTaskApp.next();
AppEvents.refreshTaskSchedule.next();
}}
/>
)}
Expand All @@ -305,7 +305,7 @@ export const TaskSchedule = () => {
onClose={() => {
setOpenCreateTaskForm(false);
setEventScope(EventScopes.CURRENT);
AppEvents.refreshTaskApp.next();
AppEvents.refreshTaskSchedule.next();
}}
submitTasks={submitTasks}
onSuccess={() => {
Expand Down

0 comments on commit 286b741

Please sign in to comment.