LIMS — 검체 분석 워크플로우 관리
분석 파이프라인의 디커플링·검체 lifecycle 동시성 안전·장시간 작업의 시간 경계 정책을 동시에 만족시키도록 운영 중에도 단계적으로 정렬해 온 LIMS 백엔드
기술 스택
개요
의뢰인이 보낸 검체를 접수해 분석·결과 검증·보고서 전달까지 다루는 실험실 운영 워크플로우를 디지털화하는 LIMS(Laboratory Information Management System) 백엔드입니다. 통상적인 LIMS와 마찬가지로 검체 등록 → 바코드 부여 → 시료 전처리 → 검사 실행 → QC → 결과 분석 → 보고서 생성 → 결과 전달의 전체 lifecycle을 단일 시스템에서 다루고, 모든 상태 전이는 chain of custody 차원에서 추적 가능해야 합니다.
LIMS는 일반 웹 서비스보다 까다로운 운영 특성을 갖습니다 — 분석 파이프라인은 원격 호스트에서 수십 분~몇 시간 단위로 도는 장시간 작업이고, 한 검체의 lifecycle은 여러 운영자가 병렬로 다루는 다단계 상태 전이이며, 외부 분석 호스트나 NFS가 정지하면 작업이 무한 대기로 적체됩니다. 백엔드 운영을 이어가면서 분석 파이프라인의 디커플링, 검체 lifecycle의 동시성 안전, 장시간 작업의 시간 경계 정책 세 가지를 핵심 결정 기준으로 잡고 그에 맞춰 구조를 정렬해 왔습니다.
기술 스택
- Backend: Python, Django 4.2, DRF 3.16, PostgreSQL 15 (psycopg3)
- Auth/보안: JWT (simplejwt), IP allowlist 미들웨어
- Infra: Celery 5.3, Redis, NGINX, Docker Compose (local / production / deploy 3환경)
역할
Backend Developer로 참여해 분석 파이프라인 dispatch 구조 정비, 검체 lifecycle state machine 정합성, 큐·워커 격리, 시스템 로그·식별자 체계 정리까지 서버 측 운영 전반을 담당하고 있습니다. 운영 중 안정성을 해치지 않는 선에서 LIMS 도메인 요건을 단계적으로 반영하는 것이 일관된 작업 방식입니다.
주요 기여
-
원격 분석 파이프라인을 띄우기만 하고 결과는 따로 감지하는 구조로 분리.
- 분석은 원격 분석 호스트에서 수십 분 ~ 몇 시간이 걸리는 작업이라, 백엔드가 SSH로 접속해 결과까지 기다리는 구조는 네트워크가 한 번만 끊어져도 분석이 통째로 죽습니다. SSH 키 기반 인증으로 바꾸고 원격 명령을 tmux 세션 안에서 띄워 백엔드의 SSH 연결 수명에서 분석 수명을 떼어 냈고, 백엔드는 명령을 띄우는 것까지만 기다리고 곧바로 리턴합니다. 완료 감지는 원격 호스트가 공유 스토리지(NFS)에 결과 파일을 떨어뜨리면 별도의 폴링 작업이 그걸 잡아내는 방식이고, 12시간이 지나도 결과 파일이 안 떨어지면 자동으로 실패 상태로 전환해 무한 대기를 시스템 차원에서 끊습니다.
-
검체 수명주기의 15개 상태 전이를 행 단위 잠금으로 동시성 안전하게 운영.
- 검체는 접수 → 품질검사(QC) → 실험 → 분석 → 결과 검증 → 전달까지 15개의 명시적 상태를 거치고, 재실험·재샘플링 같은 우회 분기까지 같은 시스템에서 다뤄야 합니다. 외부 상태머신 라이브러리에 의존하지 않고 서비스 레이어에 상태별 전이 함수를 모아 두고, 각 전이는 트랜잭션 안에서 해당 검체 행에 잠금을 잡고 진행하도록 묶었습니다. 여러 운영자가 같은 검체에 동시에 작업해도 두 전이가 겹치지 않게 데이터 계층에서 직렬화한 결정이며, 재실험·재샘플링은 진행 중이던 분석을 깨끗이 되감는 보상 처리로 다뤘습니다.
-
장시간 보고서 작업과 인터랙티브 작업을 워커 단계에서 격리.
- PDF·Excel 보고서 생성은 분 단위로 걸리는 작업이라 단일 큐에 두면 검체 접수·QC 승인 같은 즉시 응답 작업까지 함께 막힙니다. 보고서 작업을 전용 큐로 라우팅하고 그 큐만 소비하는 전용 워커(동시성 1)를 따로 두어 — 결과가 늦게 와도 괜찮은 작업과 즉시 응답이 필요한 작업을 워커 풀 단계에서 격리했습니다. 일반 워커에도 1시간 제한 시간과 메모리 한도를 두어 한 작업이 풀 전체를 점유하지 못하게 했습니다.
트러블슈팅
-
장시간 분석 파이프라인 중간에 SSH 세션이 끊겨 분석이 통째로 죽던 문제.
- 문제: 초기에는 패스워드 기반 SSH로 원격 분석 호스트에 접속한 뒤 명령이 끝날 때까지 백엔드가 SSH 세션을 들고 있는 동기 호출 구조였습니다. 분석은 수십 분에서 몇 시간이 걸리는데, 실험실 네트워크의 짧은 끊김이나 idle timeout 한 번에 세션이 끊기면 그 위에서 돌고 있던 분석이 통째로 죽고 백엔드 작업은 행 상태로 남거나 강제 회수됐습니다. 비밀번호를 외부 명령에 흘려보내야 한다는 보안상 약점과 SSH 세션 수명이 분석 수명과 묶여 있다는 구조적 약점이 같이 누적된 상태였습니다.
- 해결: 두 단계로 다시 짰습니다. (1) SSH 키 기반 인증으로 전환해 비밀번호를 외부 명령에 노출하지 않게 하고, (2) 원격 명령을 tmux 세션 안에서 띄워 백엔드의 SSH 연결 수명에서 파이프라인 수명을 떼어 냈습니다. 백엔드는 명령을 띄우는 것까지만 기다리고 곧바로 리턴하며, 완료 감지는 공유 스토리지(NFS)에 떨어진 결과 파일을 폴링 작업이 따로 잡아내도록 했습니다. SSH가 도중에 끊겨도 tmux 안에서 파이프라인은 계속 도므로, 네트워크 안정성이나 백엔드 재기동 어느 쪽에도 분석 성공이 묶이지 않게 됐습니다.
-
여러 보고서 마무리 작업이 같은 검체 상태를 동시에 갱신하다 옛 값을 덮어쓰던 race.
- 문제: 한 검체의 보고서 생성이 끝나면 검체 상태를 “전달 완료”로 전이시키는데, 같은 검체에 대해 보고서 작업이 병렬로 마무리되는 환경에서 첫 작업이 들고 있던 옛 상태 스냅샷이 두 번째 작업의 갱신과 race해 — 잘못된 이전 상태(“분석 완료” 등)를 기준으로 마무리되는 일이 있었습니다. 일반적인 ORM의 새로고침은 잠금 없이 그 시점의 스냅샷만 가져오기 때문에, 동시 마무리 환경에서는 충분히 안전하지 않았습니다.
- 해결: 상태 전이 직전에 해당 검체 행에 잠금을 잡고 트랜잭션 안에서 갱신하도록 묶어, 같은 검체에 대한 두 마무리 작업이 자동으로 직렬화되게 했습니다. 같은 패턴을 검사 배치 생성 시 Experiment·Analysis 일괄 생성 경로에도 적용해 동시 배치 생성으로 인한 중복 Analysis 행 발생도 함께 막았습니다.
의의
장시간 분석 파이프라인, 다단계 검체 lifecycle, chain of custody 요건이 동시에 작용하는 LIMS 도메인에서, 백엔드 구조를 디커플링·동시성 안전·시간 경계 세 축으로 정렬해 본 사례입니다. “SSH 세션에 분석 수명을 묶지 않는다”, “검체 상태 전이는 row-level lock으로 직렬화한다”, “장시간 작업은 시스템 차원의 timeout 가드로 끊는다” 같은 결정들이 같은 시스템 안에서 일관되게 굴러가도록 정렬한 점이 가장 만족스러웠습니다.