# 자주 묻는 것

수집 흐름을 보다가 막히는 지점들을 모았다. 답은 전부 코드와 실제 실행으로 확인한 것이다.

용어가 낯설면 `01-temporal-개념정리.md`의 용어 대조표를 먼저 본다. 화면에 나오는 영어 용어는 같은 문서의 "화면에서 무엇을 보나"에 정리돼 있다.

## 흐름이 안 돈다

### 야간 수집이 아예 시작되지 않았다

스케줄러 쪽부터 본다. 순서대로 셋을 확인한다.

1. 스케줄러 파드가 떠 있나
2. 그 파드에 스케줄 켜기 설정이 들어갔나
3. 수집 모듈 경로가 등록 목록에 들어갔나

3번을 놓치기 쉽다. 설정만 켜고 목록에 안 넣으면 스케줄러는 정상적으로 뜨는데 등록된 작업이 0개다. 로그에 "scheduler started: 0 jobs"로 나온다.

스케줄러가 정상이면 Temporal 화면에서 그 시각에 실행이 만들어졌는지 본다. 실행이 있으면 스케줄러 문제가 아니다.

### 실행은 만들어졌는데 아무 일도 안 일어난다

**워커가 없을 때 나타나는 전형적인 모습이다.** Temporal은 워커가 없어도 오류를 내지 않는다. 그냥 대기한다.

화면에서 실행을 열고 Workers 탭을 본다. 그 줄을 받아가는 워커가 하나도 없으면 이 경우다.

어느 워커가 없는지는 멈춘 지점으로 갈린다.

| 멈춘 지점 | 없는 워커 |
|---|---|
| 시작 직후 아무것도 안 함 | core 워커 |
| 계획 단계에서 멈춤 | 해당 CSP 수집기 |
| 수집 단계에서 멈춤 | 해당 CSP 수집기 |

### 워커는 떠 있는데 실행이 실패한다

워커가 있어도 그 이름의 작업을 모르면 실패한다. 두 경우를 구분해야 한다.

| 상황 | 결과 |
|---|---|
| 그 줄을 받는 워커가 없음 | 대기. 오류 없음 |
| 워커는 있는데 그 작업 이름을 모름 | 실패. "등록되지 않았다"는 응답 |

새 작업을 추가하고 워커 등록 목록에 안 넣으면 두 번째가 된다.

### 특정 CSP만 안 긁힌다

줄 이름이 계정의 CSP 값으로 만들어진다. 예상 밖의 값이면 아무도 안 듣는 줄로 가서 조용히 대기한다.

세 CSP 외의 값이 들어올 수 있는 경로가 몇 개 있다. `01` 문서의 미정 항목을 참고한다.

## 계정 상태가 이상하다

### 계정이 계속 "수집 중"이다

수집이 시작되고 끝나지 않았다는 뜻이다. 화면에는 오류가 아니라 진행 중으로 보인다.

원인은 대개 둘이다.

- 수집 도중 워커가 죽었다. 배포나 노드 정리 때 자주 생긴다
- 수집이 실제로 아직 돌고 있다. 큰 계정은 30분까지 걸린다

Temporal 화면에서 그 계정의 실행을 찾아 상태를 본다. 실행이 이미 끝났는데 계정만 "수집 중"이면 첫 번째다.

```
WorkflowId STARTS_WITH 'resource-collection-<계정ID>'
```

### 계정이 계속 "예약됨"이다

수집 차례가 오기 전에 실행이 중단됐다는 뜻이다. 계정은 한 번에 20개씩 도는데, 순서를 기다리다 실행 전체가 죽으면 그대로 남는다.

이 상태를 되돌리는 자동 장치가 없다. 다음 수집이 돌아야 갱신된다.

### 전체 결과는 "실패 0건"인데 데이터가 없다

**정상이다.** 전체 결과가 세는 것은 실행 자체가 죽은 계정뿐이다.

수집 작업이 전부 실패해도 계정 실행은 정상 종료로 끝난다. 긁을 수 있는 것을 다 긁고 실패 건수를 보고하는 구조라서다.

계정 단위로 확인해야 한다.

```
CollectStatus='failed'
CollectStatus='warning'
```

### 계정이 "경고"인데 뭐가 문제인가

수집은 됐는데 일부를 못 긁었다는 뜻이다. 대부분 우리 잘못이 아니다.

실행을 열어 Result의 분류를 보면 몇 개 작업이 경고인지 나온다. Event History에서 그 작업의 오류 내용을 보면 원인이 나온다.

경고로 분류되는 것들은 `02` 문서의 실패 등급 표에 있다. 권한 부족이 가장 흔하다.

## 후처리가 안 돈다

### 매핑이 안 걸린다

매핑은 관계 정리가 끝난 뒤에 시작한다. 관계 정리가 오래 걸리거나 실패하면 매핑도 늦어진다.

다만 관계 정리가 실패해도 매핑은 진행된다. 이 경우 VPC 조건이 붙은 규칙은 이번 사이클에 안 맞고 다음 사이클에 맞는다.

매핑 실행 자체가 없으면 수집이 분기점까지 도달하지 못한 것이다.

```
WorkflowType='MappingApplyRun'
```

### 진단 결과가 갱신되지 않았다

진단은 수집이 결과를 기다리지 않고 띄우는 쪽이다. **수집이 성공으로 끝나도 진단은 실패해 있을 수 있다.**

따로 찾아봐야 한다.

```
WorkflowType='WellArchitectedRun'
```

### 티켓이 안 생긴다

티켓 정합은 진단이 성공했을 때만 이어진다. 진단이 실패하면 그 고객의 티켓은 건드리지 않는다.

진단 실행을 열어 Timeline에서 두 번째 단계까지 갔는지 본다.

## 하고 싶은 것이 있다

### 지금 당장 수집을 돌리고 싶다

계정을 지정해 요청하는 것이 정상 경로다. 요청당 100개까지 받는다.

수동 요청도 스케줄과 같은 경로를 타므로 관계 정리, 매핑, 진단까지 전부 따라온다.

전체를 돌리려면 Temporal에서 직접 시작할 수도 있다. 스케줄러가 하는 것과 같은 일이다.

```bash
temporal workflow start \
  --type ResourceCollectionRun \
  --task-queue core-collect \
  --workflow-id resource-collection-run-<아무거나> \
  --input '{}'
```

실행 이름은 매번 달라야 한다. 돌고 있는 같은 이름이 있으면 거부된다.

### 특정 리소스 하나만 다시 긁고 싶다

**지금 계약으로는 안 된다.** 최소 단위가 계정 하나 × 서비스 그룹 하나 × 리소스 타입 하나 × 리전 하나다.

리소스 식별자를 지정하는 자리가 요청 어디에도 없다. 수집기도 타입 단위로 전체를 열거하는 구조라 한 건만 조회하지 않는다.

가장 좁게 요청하는 방법은 수집 범위에 그룹, 타입, 리전을 다 지정하는 것이다.

### 실패한 것만 보고 싶다

```bash
temporal workflow list --query "CollectStatus='failed'"
temporal workflow list --query "CollectStatus='warning'"
```

이 이름표는 계정별 수집 실행에만 붙는다. 전체 실행에는 안 붙는다.

### 과거 실행을 보고 싶다

Temporal은 실행 기록을 영구 보관하지 않는다. 보관 기간이 지나면 사라진다.

오래된 것을 보려면 실행 이력 원장을 봐야 하는데, 아직 조회 경로가 없다. 기록은 쌓이지만 DB를 직접 봐야 한다.

### 수집이 왜 멈췄는지 로그로 추적하고 싶다

어렵다. Temporal 경로 로그에는 추적 식별자가 안 박힌다.

NATS와 스케줄러 진입점은 요청마다 식별자를 붙여 한 줄로 따라갈 수 있게 감싸는데, Temporal 작업은 감싸지 않았다.

지금은 실행 식별자에 계정 ID가 들어가는 것을 단서로 삼는 수밖에 없다.

## 바꾸려고 한다

### 워크플로우를 고쳐서 배포해도 되나

돌고 있는 실행이 있으면 위험하다. 재생할 때 새 코드로 옛 기록을 재생하게 되어 순서가 어긋난다.

| 안전 | 위험 |
|---|---|
| 로그 문구 | 작업 호출 순서 |
| 시간 제한 값 | 작업 추가나 삭제 |
| 작업 내부 구현 | 조건 분기 |

버전을 나누는 장치를 쓰고 있지 않아서, 지금 대응은 야간 수집이 도는 시간대를 피하는 것뿐이다.

### 새 작업(activity)을 추가하려면

두 곳이다.

1. 작업 함수를 만든다
2. **워커 등록 목록에 넣는다**

2번을 빼면 실행이 실패한다. 워커가 그 이름을 모른다고 응답한다.

### 새 리소스 타입을 추가하려면

수집기 쪽 세 곳이다.

1. 수집 대상 선언에 타입을 추가한다
2. 그 타입의 수집 코드를 만든다
3. **조립 파일에 불러오기 한 줄을 넣는다**

3번을 빼면 오류 없이 그 타입만 안 긁힌다. 자동 탐색이 못 찾기 때문이다. 가장 잡기 어려운 실수다.

### core에 새 도메인을 추가하려면

Temporal과 무관하게 등록해야 하는 곳이 둘 있다.

- 저장 모델을 모아두는 파일
- 화면 경로를 모아두는 파일

훅이 경고는 하지만 막지는 않는다.

### 시간 제한이나 재시도를 바꾸려면

워크플로우 파일에서 그 작업을 부르는 자리를 고친다. 값이 실행 기록에 박히므로 **돌고 있는 실행에는 적용되지 않는다.** 다음 실행부터 반영된다.

### 수집 시간이 부족하다

시간 제한을 늘리기 전에 수집 자체를 줄이는 쪽을 먼저 본다. 리소스마다 추가 호출이 붙는 구조면 그것을 묶는 것이 훨씬 효과가 크다.

권한 정보 수집이 그 사례다. 리소스마다 여러 번 부르던 것을 한 번에 받아오게 바꿔서 시간 초과를 없앴다.

## 알아두면 좋은 것

### 같은 이름으로 두 번 시작하면

돌고 있는 동안에는 거부된다. 끝난 뒤에는 같은 이름을 다시 쓸 수 있다.

자식 실행 이름은 부모 이름의 뒷자리에서 만들어진다. 그래서 같은 뒷자리를 가진 부모를 다시 돌리면 자식 이름이 겹쳐 부모가 실패한다.

### 작업이 두 번 실행될 수 있나

**그렇다.** 실패했을 때만이 아니라 성공했을 때도 그렇다. 일을 끝내고 결과를 돌려주는 중에 워커가 죽으면 서버는 끝났는지 알 수 없어 다시 시킨다.

그래서 작업은 여러 번 돌아도 결과가 같아야 한다. 우리 저장 단계가 키를 정해 덮어쓰는 이유다.

### 작업 하나가 실패하면 어디까지 영향이 가나

| 실패한 것 | 영향 범위 |
|---|---|
| 수집 작업 하나 | 그 작업만. 형제는 계속 |
| 계정 하나 | 그 계정만. 다른 계정은 계속 |
| 고객 하나의 관계 정리 | 그 고객만 |
| 관계 정리 전체 | 매핑은 그대로 진행 |
| 진단 하나 | 그 고객의 티켓만 안 갱신 |

실패는 촘촘히 격리되어 있다. 대신 그만큼 조용하다.

### 배포 중에 수집이 죽으면

데이터는 안 사라진다. 다만 서버가 워커의 죽음을 알아채는 데 최대 30분이 걸린다. 진행 신호를 보내지 않기 때문이다.

자세한 것은 `01` 문서의 파드 종료 절차 부분을 본다.

### DB 트랜잭션은 어디서 끊기나

작업 하나가 트랜잭션 하나다. 작업이 성공하면 그 자리에서 저장되고, 실패하면 되돌려진다.

여러 작업에 걸친 트랜잭션은 없다. 그래서 중간에 실패하면 앞 작업의 결과는 남는다. 이걸 전제로 각 작업이 여러 번 돌아도 안전하게 만들어져 있다.
