본문으로 건너뛰기

CLI 응답 지연과 연결 진단

tasty 명령의 응답이 늦거나, 연결이 안 되거나, Error (-32061) 같은 오류로 끝날 때 보는 페이지입니다. 응답 대기 상한과 작업 대기 시간의 차이, 오류 코드별 조치, 이 Tasty가 지원하는 기능 확인, tasty list pressure 로 보는 연결 수·요청 큐·데이터베이스 상태를 다룹니다. 처음 CLI를 쓴다면 tasty CLI 로 터미널 조작하기부터 보세요.

응답 대기와 작업 대기 구분

  • tasty --response-timeout-ms MS <명령> 은 CLI가 Tasty의 응답을 기다리는 상한입니다. 명령 앞에 적습니다.
  • tasty agent task-await --timeout-ms MS 는 작업이 끝나기를 기다리는 시간입니다. 생략하면 10분, 0 이면 무한정 기다립니다. 결과는 응답의 outcome 으로 알려 주며 시간 초과도 종료 코드 0 입니다(작업 기다리기와 관리).
  • tasty pty state 와 tasty agent barrier-state 는 기다리지 않고 지금 상태를 바로 돌려줍니다. 기다리려면 다시 호출합니다.

두 시간을 함께 줄 때 응답 대기 상한이 작업 대기보다 짧으면, 작업이 끝나기 전에 CLI가 Error (-32061) 로 끝납니다. 응답 대기 상한을 작업 대기보다 길게 잡으세요.

응답 대기에 상한 걸기

tasty --response-timeout-ms 5000 read screen --surface 42

--response-timeout-ms 는 명령 앞에 적습니다. 그 시간 안에 답이 안 오면 명령이 Error (-32061): … 로 끝납니다. 이 오류는 결과를 모른다는 뜻입니다 — 요청은 Tasty 안에서 계속 실행될 수 있으므로, 무언가를 바꾸는 명령이었다면 다시 보내기 전에 상태를 먼저 확인합니다. 요청이 Tasty 안에서 차례를 기다리는 동안 시간이 다 됐다면 대신 Error (-32067): … 로 끝납니다. 이때는 아무것도 실행되지 않았으므로 그대로 다시 보내면 됩니다. 플래그를 안 주거나 0 을 주면 상한 없이 기다립니다.

요청 하나로 끝나는 명령에만 쓸 수 있고, events follow 처럼 반복해서 묻거나 원격 attach 처럼 연결을 여는 명령은 이 플래그를 받으면 아무것도 보내지 않고 종료 코드 2 로 거절합니다.

상한을 이해하지 못하는 이전 버전의 Tasty 에는 요청을 보내지 않습니다. 이때 명령은 stderr 에 {"error":{"kind":"unsupported_capability",…,"sent":false}} 한 줄을 쓰고 종료 코드 1 로 끝납니다(이 Tasty가 지원하는 기능 확인). 그 확인도 같은 시간 안에 들어갑니다 — Tasty 가 멈춰 있어 확인조차 제시간에 안 끝나면 요청을 보내지 않고 Error (-32067): … 로 끝나므로, 그때도 그대로 다시 보내면 됩니다.

이 Tasty가 지원하는 기능 확인

list info는 이 Tasty가 무엇을 할 줄 아는지도 capabilities로 함께 답합니다. 이름과 버전이 짝으로 오며, 버전 문자열만으로는 알 수 없는 것을 여기서 확인합니다 — 같은 버전이라도 빌드 구성에 따라 할 수 있는 일이 다릅니다. 모르는 이름은 그냥 무시하면 됩니다.

명령이 "sent":false 가 든 오류 한 줄로 끝나면 연결된 Tasty가 그 기능을 모르는 이전 버전이라 요청을 보내지 않은 것입니다. capability 에 적힌 이름이 무엇이 없는지 알려 줍니다.

오류별 조치

  • "sent":false 가 든 오류 한 줄이 나옵니다 — 연결된 Tasty 가 그 기능(읽기 위치 지정, 응답 대기 상한 등)을 모르는 이전 버전입니다. 요청은 보내지 않았습니다. capability 에 적힌 이름이 무엇이 없는지를 말합니다.
  • Error (…) 줄 아래에 data: {…} 줄이 붙습니다 — Tasty 가 실패의 분류를 함께 보냈습니다(예: 메모리 저장 실패의 storage_failure, 거절 사유의 reason). data: 뒤는 한 줄 JSON 이라 스크립트는 첫 줄 대신 이 값으로 분기할 수 있습니다. 첫 줄 Error (코드): 메시지 는 이 줄이 있든 없든 같습니다. tasty events follow 와 tasty plugin audit-follow 도 이 줄을 붙이며, 두 명령의 첫 줄은 Error: Error (코드): 메시지 로 앞에 Error: 가 하나 더 붙은 모양 그대로입니다.
  • Error (-32603): memory writer queue is full … 로 끝납니다 — 메모리 저장 요청이 한꺼번에 몰려 Tasty 가 이 요청을 받지 않았습니다(data 의 storage_failure 가 busy). 저장되지 않았으므로 잠시 뒤 다시 부르면 됩니다. memory writer is not accepting requests 는 Tasty 가 종료 중이라는 뜻입니다.
  • 세션 토큰을 쓰는 에이전트의 요청이 Error (-32000): host is busy: the request gate could not reach the memory writer … 로 끝납니다 — 요청을 받기 전에 확인하는 세션 판정을 Tasty 가 저장소에 넣지 못했습니다(data 의 storage_failure 가 busy). 요청은 실행되지 않았으므로 잠시 뒤 그대로 다시 부르면 됩니다. Error (-32000): host is shutting down 은 Tasty 가 종료를 시작해 요청을 실행하지 않았다는 뜻입니다. 토큰 없이 부르는 CLI 명령은 이 확인을 거치지 않습니다.
  • 메모리·에이전트 협업 명령이 "outcome":"unknown" 이 든 오류로 끝납니다 — 저장이 끝났는지 Tasty 도 확인하지 못했습니다. 같은 요청을 다시 보내기 전에 tasty memory get 등으로 값을 확인합니다. 응답을 기다리다 명령을 중단해도 이미 받은 저장 요청은 취소되지 않습니다.
  • Error (-32065): … 로 끝납니다 — Tasty 가 처리할 요청이 밀려 있어 이 요청을 받지 않았습니다. 요청은 실행되지 않았으므로 잠시 뒤 그대로 다시 부르면 됩니다.
  • Error (-32066): … 로 끝납니다 — 연결한 뒤 20 초 안에 요청을 보내지 않아 Tasty 가 연결을 닫았습니다. 아무것도 실행되지 않았습니다. 직접 소켓을 여는 도구라면 연결 직후 바로 요청을 보냅니다. 한 번 요청을 보낸 연결은 요청 사이에 오래 쉬어도 닫히지 않습니다.

요청 처리 규칙

세션 토큰을 사용하는 에이전트의 호출 한도는 일반 명령뿐 아니라 플러그인 명령과 전체 목록 조회에도 적용됩니다. 허용된 요청은 호출량에 한 번 기록되며, 한도를 넘으면 요청을 실행하지 않고 오류로 답합니다. 토큰 없는 로컬 CLI의 기존 예외는 유지됩니다.

플러그인 namespace 호출은 활성화된 소유 플러그인과 해당 IPC 훅에 필요한 활성 확장만 시작합니다. 비활성 플러그인을 켜거나 무관한 플러그인을 시작하지 않습니다. 없는 namespace는 아무것도 시작하지 않으며, 알려진 namespace 안의 메서드 오타는 소유 플러그인을 시작한 뒤 오류를 반환할 수 있습니다.

구조 변경 요청이 대기열 또는 저장 예산 한도를 넘으면 -32065 오류를 받습니다. 다른 요청이 잠시 사용 중인 저장 예약분이 부족한 경우에는 한도 안에서 기다립니다. 동일 멱등 키에 다른 요청을 보내면 -32063 오류를 받습니다.

한 윈도우의 구조 변경이 외부 작업 완료를 기다리는 동안에도, 대상을 명시한 다른 윈도우의 독립적인 구조 변경 요청은 진행할 수 있습니다. 같은 대상 범위의 요청 순서는 유지됩니다.

응답 지연과 연결 상태 확인

tasty list pressure는 요청 대기·처리 시간, 연결 수, 거절 사유를 보여 줍니다. 항목마다 측정 대상이 다르므로 요청 수를 서로 빼서 거절 수를 계산하지 마세요. 누계는 인스턴스 시작 후 쌓이며, 측정한 적 없는 평균은 null입니다. 현재 사용량과 설정값은 아래에서 별도로 구분합니다.

시간 항목 측정 대상
queue_before_gate 큐에서 기다린 시간. 나중에 권한 검사에서 거절된 요청도 포함
handler_after_gate 검사를 통과해 실제 처리한 요청의 시간
plugin_round_trip 플러그인에 보낸 요청 중 응답이 도착한 요청의 대기 시간
db 데이터베이스 commit과 WAL checkpoint에 걸린 시간, commit 하나에 저장된 쓰기 수, 따로 센 VACUUM

큐 대기가 길면 요청이 밀리는지, 처리 시간이 길면 어떤 명령이 오래 걸리는지 확인하세요. 플러그인 대기가 길면 해당 플러그인의 로그를 살펴보세요. DB 시간은 저장 작업과 경합을 조사할 근거이며 이 값만으로 디스크 문제를 단정할 수는 없습니다. commit 시간은 처리 시간에도 포함되므로 두 시간을 더하면 중복됩니다. checkpoints_busy는 다른 연결 때문에 checkpoint를 마치지 못한 횟수입니다. writes_per_commit은 commit 하나에 저장된 쓰기 수의 분포이고, commit 수에 들어가지 않는 데이터베이스 압축(VACUUM)은 vacuums로 따로 셉니다. memory_writer는 저장 요청을 모아 처리하는 대기열의 접수·거절·묶음 수입니다. state_writer와 state_db는 최근 파일과 튜토리얼 진행을 저장하는 대기열의 같은 수와 commit 시간입니다. 헤드리스 실행이나 state.db를 열지 못한 실행에서는 null입니다.

저장 대기열의 batch_wait는 묶음이 실행되기 전 대기, batch_execution은 재시도 대기까지 포함한 묶음 처리, executor_execution은 실제 처리 호출의 시간 분포입니다. 단위는 마이크로초이며 samples가 0이면 아직 관측하지 않은 것입니다. queued_items·queued_bytes는 아직 실행에 넘기지 않은 대기열만 셉니다. pending_bytes와 oldest_pending_age_us는 끝나지 않은 요청의 입력 크기와 가장 오래된 나이입니다. 미완료 요청이 없으면 나이는 null입니다. 이미 끝났지만 결과를 읽지 않은 요청은 held_bytes에 남을 수 있습니다. 이 나이는 파일의 미저장 변경 나이가 아니며, 완료 수나 묶음 수가 실제 디스크 commit 수를 뜻하지도 않습니다.

journal은 GUI와 헤드리스 모두에서 저널 작업의 접수·거절·대기·실행 중 수를 보여 줍니다. 작업 단계 수는 사용자 명령 수와 다르고, 바이트는 입력 크기 추정치입니다. store 안의 physical_commits는 실제 SQL commit 수, structural_commits는 구조 변경을 담은 commit 수, logical_requests는 확정한 논리 요청 수입니다. 시간 분포는 고정 구간에 누적됩니다. dirty_age_us가 null인 것은 이 계측이 그 시간을 제공하지 않는다는 뜻입니다. unpublished_age_us는 저장 뒤 화면 반영 확인을 기다린 시간이며 조회가 값을 비우지 않습니다. 실제 SQL commit이 실패해 확정 상태를 다시 확인해야 하면 publication_known은 false, unpublished_age_us는 null입니다. 이 경우 저널을 다시 열어 검증할 때까지 unknown으로 유지합니다. 저널이 없는 시험 구성은 journal: null, 아직 저장소가 열리지 않았으면 store: null입니다.

file_snapshots.settings는 GUI와 헤드리스 모두에서 메모리에 적용한 설정과 저장이 확인된 설정의 차이를 보여줍니다. dirty가 true이면 아직 저장이 확인되지 않았으며, dirty_age_us는 첫 미저장 변경부터 지난 마이크로초입니다. 새 변경·실패·결과 불명·재시도·이전 변경의 저장 성공으로 이 시간이 초기화되지는 않습니다. 최신 변경의 저장 성공이 확인되면 dirty는 false, 나이는 null이 됩니다. 실패 후 pending이 0이어도 미저장 변경은 남을 수 있습니다. 이 조회는 저장 완료 처리를 앞당기지 않습니다.

state_snapshots.recent_files는 GUI의 최근 파일 목록에 저장이 확인되지 않은 변경이 있는지(dirty)와 그 상태가 처음 시작된 뒤 지난 마이크로초(dirty_age_us)를 보여 줍니다. 다른 종류의 목록을 바꾸거나 저장이 실패·거절·결과 불명으로 끝나도 시간은 이어집니다. 모든 종류의 최신 목록이 저장됐다고 확인되면 나이가 null이 됩니다. 종류별 나이를 따로 표시하는 값은 아니며, 조회가 저장 결과를 수거하지도 않습니다. 헤드리스나 상태 DB를 열지 못한 실행에서는 이 목록 상태가 null입니다.

권한·한도·빈도 제한의 거절 수는 아래 gate_refusals에서 확인합니다.

연결 수와 한도 (connections)

필드 의미
live 현재 연결 수. 연결이 닫히면 감소
live_max 시작 이후 가장 많았던 동시 연결 수
limit 허용하는 동시 연결 수의 상한
accepted 받아들인 연결 수의 누계
refused_saturated 연결 상한 때문에 거절한 수의 누계

요청을 보내지 않은 연결도 live에 포함됩니다. live가 limit에 가깝거나 refused_saturated가 증가하면 사용하지 않는 attach 등 지속 연결을 정리하세요. 이 거절은 요청 처리 전에 발생하므로 요청 처리 시간에 나타나지 않습니다. 연결 부족과 처리 지연은 동시에 생길 수도 있습니다.

accept_wait_bound_us_max와 accept_wait_bound_us_mean은 연결 수락 대기 시간의 상한을 최대·평균으로 보여 줍니다. accept_waits는 기록 수이며 accept_wait_bound_us_sum은 상한들의 합입니다. 실제 대기 시간을 직접 잰 값은 아닙니다. 연결 대기열이 마지막으로 비어 있던 시점부터 계산하므로 실제 대기는 이 값보다 짧을 수 있습니다. 빈 대기열을 확인한 뒤 기본 100ms 동안 기다리지만, 100ms가 전체 대기의 엄격한 상한은 아닙니다. 이 시간은 요청 큐 대기와 별개이며 평균은 관측이 쌓이면서 내려갈 수도 있습니다.

시간 분포

앞의 세 시간 항목에만 wait_us_hist 또는 us_hist가 있습니다. bounds_us는 10µs부터 1초까지의 11개 구간 경계이며 counts는 구간별 건수입니다. 마지막은 1초 초과 구간이어서 counts가 한 칸 더 많습니다. 구간은 겹치지 않으며 합계는 관측 수입니다. 가장 긴 시간은 해당 항목의 max 값으로 확인합니다. p99 같은 분위수는 제공하지 않습니다.

queue_before_gate.waits는 대기를 잰 명령 수이며 평균 계산의 분모입니다. commands는 한 묶음의 처리가 끝나야 증가하므로 진행 중인 묶음만큼 waits보다 작을 수 있습니다. 평균과 분포를 함께 보면 전반적인 지연과 일부 요청만 오래 걸리는 경우를 구분할 수 있습니다.

데이터베이스 설정 (db_pragmas)

memory_db와 state_db마다 요청한 설정(requested)과 실제 적용값(effective)을 보여 줍니다. DB를 열 때 확인한 설정이며 누적 카운터가 아닙니다.

값 의미
degraded: true 설정이 기대한 상태가 아니거나 파일 DB 대신 임시 메모리로 동작 중
state_db: null 이 인스턴스에서 상태 DB가 열려 있지 않음. headless에서도 이 값 사용
memory_db.init_failure 파일 DB를 열지 못해 임시 메모리로 바꾼 원인(cause)과 오류(error). 정상일 때는 null

설정 불일치가 있어도 DB는 계속 사용할 수 있습니다. 성능과 장애 후 복구에 영향을 줄 수 있으므로 requested와 effective를 비교하세요. 임시 메모리는 설정이 모두 적용돼도 degraded가 true이며, 그동안 저장한 값은 재시작 후 사라집니다.

화면 전송 (stream_push)

필드 의미
frames_dropped 수신자가 따라오지 못해 버린 프레임 수의 누계
clients_lagged_out 지연이 커져 연결을 끊은 수신자 수의 누계
backlog 현재 전송을 기다리는 프레임 수. 전송되면 감소
sink_capacity 연결 하나가 보관할 수 있는 프레임 수의 상한

frames_dropped가 늘면 attach가 화면 일부를 놓쳤을 수 있습니다. Tasty attach는 재연결 요청을 받으면 다시 연결해 화면을 새로 받습니다.

요청 큐 (queue_admission, queue_dispatch)

queue_admission은 큐에 들어온 양과 들어오기 전에 거절한 요청을 보여 줍니다.

필드 의미
queued_bytes, queued_commands, queued_injected 현재 대기 바이트·명령·내부 명령 수
peak_bytes 시작 이후 가장 컸던 대기 바이트 수
limit_bytes, limit_injected_depth 바이트 수와 내부 명령 수의 상한
refused_bytes, refused_depth 각 상한 때문에 거절한 요청 수의 누계

큐가 가득 찼다는 오류가 나면 이 값을 확인하세요. 큐에 들어오기 전에 거절한 요청은 큐 대기·실행 시간에 포함되지 않습니다.

queue_dispatch는 큐에서 꺼내 처리하는 과정을 보여 줍니다.

필드 의미
rounds 명령을 꺼내 처리한 횟수
rounds_stopped_by_count, rounds_stopped_by_time 한 번에 처리할 개수·시간을 다 써서 멈춘 횟수
expired_before_run 큐에서 기다리다 요청자의 제한시간이 지나 실행하지 않은 수
started 실행을 시작한 명령 수
in_flight, in_flight_max 실행을 시작한 뒤 명령이나 응답 대기가 아직 끝나지 않은 요청의 현재 수와 최댓값

in_flight는 감소할 수 있습니다. 호출자가 기다리기를 멈춰도 명령이 실행 중이면 계속 집계합니다. 시간 제한으로 멈춘 횟수나 expired_before_run이 늘면 큐 처리가 요청을 따라가지 못하는지 확인하세요.

분류 만들기·이름 변경·삭제·순서 변경과 워크스페이스 정보·순서 변경은 같은 재시도 키로 요청하면 앱 재시작 뒤에도 처음 결과를 돌려줍니다. 대상이 나중에 이동하거나 이름이 바뀌어도 새 대상을 다시 고르지 않습니다. 원격 화면의 로컬 표시 이름과 위치는 저장 결과를 다시 받을 때 재적용하지 않습니다. 다른 명령까지 재시작 후 중복 방지가 보장되는 것은 아닙니다.

중복 요청 처리 (keyed_requests)

멱등 키(idempotency_key)를 붙인 요청만 세며 요청 하나는 한 번만 집계합니다.

필드 처리 결과
executed 새 키의 요청을 실행
replayed 같은 요청에 이전 결과를 반환하고 다시 실행하지 않음
conflicted 같은 키에 다른 내용이 와서 실행하지 않음
discarded 실행했지만 응답은 버림
in_flight 같은 요청이 실행 중이어서 그 결과를 함께 기다림

replayed가 executed보다 빠르게 늘면 재시도가 반복되는지 확인하세요. 이 in_flight는 queue_dispatch의 현재 실행 수와 다른 집계입니다.

느린 요청 상세 (slow_requests)

큐 대기·호스트 처리·플러그인 대기의 합계가 threshold_us(100ms) 이상인 요청을 오래된 순서로 최대 capacity(32)건 보여 줍니다. 넘치면 가장 오래된 기록부터 버립니다. admitted는 시작 이후 기록한 누계이며 현재 남은 건수와의 차이가 밀려난 건수입니다.

필드 내용
request_seq Tasty가 부여한 번호. 요청의 id와는 다른 값
host method, caller, queue_wait_us, host_us, outcome(ok/error), error_code
plugin_hops 플러그인별 host_request_id, 대기 시간, ok/error/expired/cancelled 결과
total_us 측정 시간의 합계

아직 응답하지 않았다면 host의 outcome과 error_code는 null입니다. plugin_hops가 길면 플러그인 로그에서 host_request_id와 같은 요청 ID를 찾아보세요. 플러그인 오류·무응답 로그에는 id와 request_seq가 함께 나옵니다. 요청 본문과 토큰은 저장하지 않으며 pressure 조회 자체도 이 목록에서 제외합니다. 재시작하면 기록이 비고 번호도 1부터 다시 시작합니다.

요청 거절 사유 (gate_refusals)

judged는 권한 등을 검사한 요청 수이며 CLI 요청과 플러그인 호출을 포함합니다. 한 요청은 다음 사유 중 최대 하나에 집계됩니다.

필드 사유와 오류 코드
permission_denied 권한 검사에서 거절, -32001
cap_blocked 텔레메트리 한도에 도달, -32007
throttled 호출 빈도 제한에 도달, -32010

권한·사용량 한도를 확인하거나 빈도 제한이 풀릴 때까지 기다리세요. 권한 거절에는 부족한 권한뿐 아니라 플러그인·에이전트가 사용할 수 없는 명령이나 없는 명령을 호출한 경우도 포함됩니다. 뒤의 두 경우는 권한 요청으로 해결되지 않으므로 명령을 확인하세요. 세션 토큰 없는 로컬 CLI의 권한 면제는 유지됩니다.

모든 -32001 오류가 permission_denied에 들어가는 것은 아닙니다. 잘못됐거나 미등록·만료·폐기된 세션 토큰은 검사 전에 거절하므로 judged에도 들어가지 않습니다. 검사를 통과한 뒤 권한 부여 요청 등이 거절한 -32001도 이 카운터에서 제외합니다. 이 누계는 재시작하면 0이 됩니다. tasty agent rate-limit-status의 throttled_count는 개별 제한의 영속 누계이므로 서로 다른 값입니다.

함께 살펴보기

GitHub에서 원문 보기