예약해 둔 작업이 시작될 때 내가 다른 프로젝트를 보고 있을 수 있다. 현재 화면을 실행 대상으로 쓰면 작업 시간이 맞아도 엉뚱한 곳에 결과가 들어간다. 나는 예약을 만들 때 실행할 프로젝트와 계정, 탭을 함께 남기고 시각이 됐을 때 그 관계가 여전히 맞는지 검사하게 했다.
예약에는 시각 외에 실행 대상을 적는다
ProjectJob의 context에는 profileId·workspaceId·tabId가 들어간다. 프로그램 ID와 정의 hash, 이번 inputs, Python 또는 notebook 실행 방식도 함께 저장한다. 미래 시각 하나만 보관한 뒤 그때 선택된 프로그램을 실행하는 구조가 아니다. 목록 조회도 프로젝트별로 나눠 예약의 소속을 드러낸다.
생성 경로에서는 현재 활성화된 플러그인인지 확인하고 프로그램 정의를 읽는다. 입력 키가 실제 매개변수와 정확히 맞는지도 대조한다. 이 확인이 사이트의 업무 내용까지 이해했다는 뜻은 아니다. 입력 계약과 대상 정보를 예약 문서에 담아 미래 실행에서 다시 확인할 수 있게 만든 것이다.
보이는 선택 대신 저장한 context로 들어간다
타이머가 예약을 꺼내면 inProject에 저장된 context를 넘긴다. 그 함수는 탭이 실제로 존재하고 프로젝트와 프로필이 맞는지, 시크릿 대상이 아닌지 확인한다. 프로젝트 복사가 진행 중인 경우도 실행하지 않는다. 조건이 틀렸다고 현재 보이는 비슷한 탭으로 바꾸지 않는다.
실행 중인 프로젝트의 계정을 다른 계정으로 갈아끼우는 요청도 막는다. 빈 프로젝트라면 해당 runtime의 cursor를 context에 맞추고 그 runtime 안에서 실행한다. 이 글의 초점은 화면 선택과 커서의 일반적인 차이보다 오래 보관한 예약의 대상 계약을 실행 경계에서 어떻게 다시 확인하는가에 있다.
예약 뒤 바뀐 코드는 조용히 실행하지 않는다
실행 직전에 프로그램을 다시 읽고 manifest.hash와 예약의 hash를 비교한다. 다르면 PROJECT_SCHEDULE_PROGRAM_CHANGED다. 예약을 만들 때 본 절차와 지금 파일의 절차가 달라졌는데 같은 프로그램 이름이라는 이유로 통과시키지 않는다. 실제 실행의 ID도 예약 원장에 checkpoint로 남긴다.
프로그램 해시는 정의가 같은지를 보는 근거이며 외부 사이트가 그대로라는 보장은 아니다. 같은 코드라도 로그인과 화면, 자료는 바뀔 수 있다. 나는 정의 고정과 현장 조건 확인을 분리했다. 실행 결과가 나온 뒤에는 runner가 같은 프로그램의 실행인지 확인하고 그 실행을 기다려 최종 상태로 연결한다.
프로젝트가 바쁘면 같은 자리에 겹쳐 넣지 않는다
예약 실행은 프로젝트별 차선을 먼저 얻는다. 다른 프로젝트는 병렬로 진행할 수 있지만 같은 프로젝트에 이미 실행이 있으면 두 번째 작업은 그 시점에 시작하지 않는다. 예약 메타데이터의 잠금과 실제 긴 프로그램 실행도 구분한다. 기록 하나를 갱신하는 동안만 직렬화하고 다른 프로젝트 전체를 기다리게 하지 않는다.
지정 시각보다2분 넘게 늦으면 missed로 남기는 경계도 있다. 앱이 꺼져 있던 시간을 만회하려고 한꺼번에 과거 작업을 실행하지 않는다. 이 기능은 앱이 켜진 동안 동작하는 로컬 예약이다. 서버 상시 가동이나 정확한 정시 실행을 보장하는 기능으로 소개하지 않는다.
시험에서는 다른 프로젝트를 계속 보게 했다
기존 격리 시험은 같은 프로그램을 두 프로젝트에 등록하고 같은 시각에 서로 다른 입력으로 예약한다. 두 실행이 실제로 겹친 상태를 관찰하고 완료 뒤 각 실행의 탭과 ID, 결과를 비교한다. 화면은 다른 프로젝트를 계속 보고 있었는지도 확인한다. 현재 선택 탭만 맞춘 시험과 구별되는 지점이다.
이것은 소유한 Electron 프로필과 HTTPS fixture를 사용한 기존 시험 코드의 범위다. 이 글을 쓰면서 사용자 사이트에 새 예약을 만들거나 원래 앱의 시험을 재실행하지 않았다. 공개한 차선 모듈도 메모리 소유권만 예제로 다루며 실제 예약 저장과 브라우저 실행은 포함하지 않는다.
대상이 사라지면 새로 추측하지 않는다
예약 이후 탭을 닫거나 계정 연결을 바꿀 수 있다. 그럴 때는 실패 이유를 남기고 대상 관계를 사람이 다시 확인해야 한다. 비슷한 제목의 탭을 새로 찾아 이어가면 편해 보이지만 처음 예약의 의도가 보존됐다는 근거는 사라진다. 미래 실행일수록 자동 추측을 줄여야 했다.
재시작 때 running이었던 예약은 needs_review로 바꾸고 자동 재실행하지 않는다. 앱이 종료됐어도 외부에 전달된 행동은 남아 있을 수 있기 때문이다. 내가 만들고 싶었던 것은 시간이 되면 무조건 누르는 기능보다 언제 어떤 절차가 어느 프로젝트에 전달됐는지 설명할 수 있는 예약이었다.