# 영속화

> LMSP 서비스가 상태를 저장하는 방법. 내장 SQLite 데이터베이스, 유지할 테이블, 작업과 이벤트에 대한 트랜잭션 규칙, 보존 기간, 시작 시 복구를 정의합니다.

Source: https://madeforagent.com/ko/lmsp/1.0/persistence/
Language: ko
Specification: LMSP 1.0 (draft)
Updated: 2026-11-08

## 11.1 저장소 {#storage}

서비스는 상태를 내장 [SQLite](https://www.sqlite.org/) 데이터베이스에 보관하는 **것이 좋다**. 제안하는 테이블 구성은 다음과 같다.

| 테이블 | 내용 |
| --- | --- |
| `service_config` | 서비스 정체성과 설정. |
| `clients` | 페어링된 클라이언트, 토큰 해시, 스코프. |
| `models` | 모델 메타데이터와 내용 다이제스트. |
| `responses` | 저장된 응답. |
| `tasks` | 비동기 작업. |
| `task_events` | 각 작업의 이벤트 로그. 순번 포함. |
| `idempotency_keys` | 멱등성 키, 요청 다이제스트, 결과. |

모델 파일은 데이터베이스 밖에 두어**야 한다**. 데이터베이스에는 메타데이터와 내용 다이제스트만 담는다.

테이블 이름과 레이아웃은 구현이 선택한다. 클라이언트가 관찰할 수 있고 상호 운용되어야 하는 것은 다음 두 절의 동작이다.

## 11.2 규칙 {#rules}

서비스는 어떤 저장소를 쓰든 다음을 지킨다.

- `202 Accepted`를 반환하기 전에 작업을 영속화해**야 한다**.
- 이벤트 순번과 작업 상태를 같은 트랜잭션에서 갱신해**야 한다**.
- 취소 전이를 원자적으로 만들어, 작업이 취소된 뒤 완료되는 경합을 피해**야 한다**.
- 끝난 작업과 이벤트를 설정할 수 있는 보존 기간 동안 보관하는 **것이 좋다**.
- 로그를 정리할 때 미완료 작업을 삭제해서는 **안 된다**.
- 시작할 때 남아 있는 `queued`, `loading`, `running`, `cancelling` 작업을 훑어, 각각의 재시작 정책을 적용해**야 한다**([7.5절](https://madeforagent.com/ko/lmsp/1.0/tasks.md#restart)).

## 11.3 시작 시 복구 {#recovery}

아래 표는 남아 있던 작업이 `on_restart` 정책에 따라 옮겨 가는 상태를 보여 준다.

| 시작 시 발견된 상태 | `fail` | `retry` | `resume` |
| --- | --- | --- | --- |
| `queued` | `failed` | `queued` | `queued` |
| `loading` | `failed` | `queued` | `queued` |
| `running` | `failed` | `queued` | `running`(스냅샷에서 이어서) |
| `cancelling` | `cancelled` | `cancelled` | `cancelled` |

`failed`로 옮겨진 작업은 오류 코드 `server_restarted`와 `response.failed` 이벤트를 받는다. `cancelled`로 옮겨진 작업은 `response.cancelled` 이벤트를 받는다. 새 상태와 그 이벤트는 하나의 트랜잭션으로 기록된다([11.2절](https://madeforagent.com/ko/lmsp/1.0/persistence.md#rules)).
