ES /docs

StandardError - wrong number of arguments (given 1, expected 0)

RCA: StandardError - wrong number of arguments (given 1, expected 0)

Overview#

What Happened#

Facility Transfer 작업 중 Progress 레코드를 Elasticsearch에 재색인하는 단계에서 ProgressSerializerArgumentError: wrong number of arguments (given 1, expected 0)를 던졌다. cupixworks-worker (Sidekiq) 환경에서 2026-06-06 ~ 2026-06-08 사이 총 20건이 발생했고, 모두 BulkIndexWorker로 재시도된 뒤 Sidekiq job died after all retries 상태로 종료되었다. 직접적 원인은 progress_serializer.rb:91attribute :record_completed_at, &:record_complete_at 작성 패턴이 Symbol#to_proc의 arity 동작 때문에 fast_jsonapi가 메서드를 1-인자(serialization_params)로 호출하게 만들기 때문이다.

Quick Facts#

Field Value
exception.class StandardError (실제 원인은 ArgumentError)
exception.message wrong number of arguments (given 1, expected 0)
top_frame app/models/concerns/properties/progress.rb:91:in 'record_complete_at'
serializer app/serializers/progress_serializer.rb:91
runtime Ruby 3.3 / Rails (tesla repo)
deploy production-us-west-2-20260605T0515Z0-8562b202-cupixworks
env production / us-west-2
trigger Facility Transfer ([Transfer][854] ... fqgvj ...) → Progress re-index

Affected Teams#

Team / Domain Error Count Impact
cupixworks-worker (Progress indexing / Facility Transfer) 20 Transfer 대상 facility의 Progress 레코드가 Elasticsearch에 갱신되지 않아 검색·통계 결과가 stale 상태가 됨. Sidekiq job들이 retry 후 dead 큐로 이동.

Timeline#

  1. 2026-06-06 20:13 KST — 첫 발생 (first_seen). [Transfer][854] Facility Transfer 작업 도중 Progress _update_document 실패.
  2. 2026-06-06 20:13 ~ 21:00 KST — 첫 번째 burst (Transfer 854 진행 중, 12건).
  3. 2026-06-07 21:57 KST — 두 번째 발생 묶음 (2건).
  4. 2026-06-08 10:22 ~ 10:26 KST — 세 번째 burst, 마지막 발생(last_seen)에서 Sidekiq job died after all retries 기록.

Error Log#

Datadog Logs

text
StandardError - wrong number of arguments (given 1, expected 0)

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 20
  • 최초 발생: 2026-06-06 20:13 KST
  • 최근 발생: 2026-06-08 10:26 KST
  • 영향: Facility Transfer 도중 Progress 레코드 인덱싱 실패. _update_document 1차 실패 후 BulkIndexWorker.perform_async(..., 'index')로 재시도되지만 동일한 직렬화 경로를 거치므로 retry 도 모두 실패하여 Sidekiq job died after all retries로 종결. Elasticsearch 인덱스의 Progress 도큐먼트가 transfer 이후 최신 상태로 동기화되지 않아 검색·필터·집계 결과에 stale 데이터가 노출될 수 있음.

Root Cause Summary#

ProgressSerializer에서 attribute :record_completed_at, &:record_complete_at로 작성한 부분이 원인이다. Ruby에서 &:method_nameSymbol#to_proc을 호출하여 Proc을 만들고, 이 Proc의 arity는 Ruby 2.7+ 에서 -2이다 (->(receiver, *args) { receiver.method_name(*args) }). fast_jsonapi 1.5의 Attribute#serializemethod.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params) 분기로 호출 인자 수를 결정하는데, (-2).abs == 2 != 1이므로 항상 2-인자 분기를 타고 record.record_complete_at(serialization_params)로 호출된다. 그러나 Properties::Progress#record_complete_at은 0-인자 메서드이므로 ArgumentError: wrong number of arguments (given 1, expected 0)이 발생한다. 이 코드는 2023-04-11 (commit 1f816867, TSLA-5369)부터 존재했으며, 평소에는 as_indexed_json이 호출되지 않거나 호출되더라도 다른 직렬화 경로를 타서 발생하지 않았으나, [Transfer][854] Facility Transfer 작업이 모든 Progress 레코드의 _update_documentas_indexed_json 경로를 강제하면서 수면 위로 드러났다.

Technical Analysis#

Code Path#

  • Entry point: Facility Transfer 워커 → Progress#_update_document
  • searchable.rb:55 _update_document → 변경된 컬럼이 cached/sys에 포함되면 as_indexed_json 호출
  • searchable/progress.rb:232 as_indexed_jsonProgressSerializer.new(self, {...}) 생성
  • progress_serializer.rb:91 직렬화 시도
  • fast_jsonapi/attribute.rb:14 Proc 호출 분기 → 2-인자 호출
  • Failure point: progress.rb:91 record_complete_at (0-인자 메서드 호출에 1 인자 전달)
  • searchable.rb:118 rescue StandardError => eCupix::Logger.error("StandardError - #{e.message}", ...)BulkIndexWorker.perform_async(self.class.name, [id], 'index')
  • 재시도(BulkIndexWorker)도 동일한 직렬화 경로를 타기 때문에 모두 실패 → Sidekiq job died after all retries
app/serializers/progress_serializer.rb:91ruby
attribute :record_completed_at, &:record_complete_at
app/models/concerns/properties/progress.rb:91-99ruby
def record_complete_at
  self.sys[:record_complete_at]
end

def record_complete_at=(val)
  if self.sys[:record_complete_at].nil? || val < self.sys[:record_complete_at]
    self.sys[:record_complete_at] = val
  end
end
vendor/bundle/.../fast_jsonapi/attribute.rb:11-19ruby
def serialize(record, serialization_params, output_hash)
  if include_attribute?(record, serialization_params)
    output_hash[key] = if method.is_a?(Proc)
      method.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params)
    else
      record.public_send(method)
    end
  end
end
app/models/concerns/searchable/progress.rb:232-244ruby
def as_indexed_json(options = {})
  serializer = ProgressSerializer.new(self, {
    is_collection: false,
    fields: {
      progress: %i[
        id
        name
        ...
      ]
    }
  })
end
app/models/concerns/searchable.rb:115-121ruby
rescue Elasticsearch::Transport::Transport::Error => e
  Cupix::Logger.error("ElasticsearchError - #{e.message}", class: self.class.name, function: __method__)
  BulkIndexWorker.perform_async(self.class.name, [id], 'index')
rescue StandardError => e
  Cupix::Logger.error("StandardError - #{e.message}", class: self.class.name, function: __method__)
  BulkIndexWorker.perform_async(self.class.name, [id], 'index')
end

기대 동작: 직렬화 시 record_complete_at가 인자 없이 호출되어 self.sys[:record_complete_at] 값이 반환되어야 한다. 실제 동작: fast_jsonapi가 Symbol#to_proc이 만든 Proc의 arity(-2)를 보고 2-인자 분기를 선택, record_complete_at(serialization_params)로 호출 → ArgumentError 발생.

Log Evidence#

Datadog 쿼리:

text
service:cupixworks-worker status:error "wrong number of arguments (given 1, expected 0)"

Same-request 컨텍스트 쿼리:

text
service:cupixworks-worker @request_id:3a78378665da18573e8f5297

Sidekiq death handler 로그(스택트레이스 포함):

json
{
  "timestamp": "2026-06-08 10:26:33",
  "status": "error",
  "message": "Sidekiq job died after all retries",
  "class": "SidekiqDeathHandler",
  "function": "death_handler",
  "error": {
    "msg": "wrong number of arguments (given 1, expected 0)",
    "stack": [
      "/var/app/current/app/models/concerns/properties/progress.rb:91:in `record_complete_at'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/attribute.rb:14:in `serialize'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/serialization_core.rb:48:in `block in attributes_hash'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/serialization_core.rb:47:in `each'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/serialization_core.rb:47:in `each_with_object'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/serialization_core.rb:47:in `attributes_hash'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/serialization_core.rb:80:in `record_hash'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/object_serializer.rb:46:in `hash_for_one_record'",
      "/var/app/current/vendor/bundle/ruby/3.3.0/gems/fast_jsonapi-1.5/lib/fast_jsonapi/object_serializer.rb:35:in `serializable_hash'",
      "/var/app/current/app/models/concerns/searchable.rb:588:in `to_searchable_json'"
    ]
  }
}

Trigger 컨텍스트 (같은 request_id로 직전에 발생한 Transfer 로그):

text
:exclamation: [Facility Transfer][854] 1단지 211동(fqgvj) from 이문1구역 주택재개발 정비사업(2912) to 주택(5719) in error.
[Transfer][854][60] Finalizing Progress on facility fqgvj

직렬화 실패 → rescue 분기 → BulkIndexWorker 재시도 흐름:

text
StandardError - wrong number of arguments (given 1, expected 0)   (class: Progress, function: _update_document)
Index error - wrong number of arguments (given 1, expected 0)     (class: Progress, function: _index_document)
NotFound - attributes_in_database                                 (class: Progress, function: _update_document)

발생 패턴 (모두 Facility Transfer burst와 일치):

text
2026-06-06 20:13 ~ 22:59 KST  (Transfer 854 첫 burst, 12건)
2026-06-07 21:57 KST          (2건)
2026-06-08 10:22 ~ 10:26 KST  (5건, 마지막에 Sidekiq dead)

코드 도입 commit (참고):

text
commit 1f816867e3ef8bfa3be4ce8e8f786194df53d905
Author: eddy.lee <eddy.lee@cupix.com>
Date:   Tue Apr 11 10:50:01 2023 +0900
TSLA-5369 fix scroll
+  attribute :record_completed_at, &:record_complete_at

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 &:record_complete_at 형태로 등록한 직렬화 attribute가 Symbol#to_proc.arity == -2이기 때문에 fast_jsonapi 1.5의 Attribute#serializeserialization_params를 1-인자로 전달하여 0-인자 메서드 호출에 실패 스택트레이스 최상단 progress.rb:91:in 'record_complete_at' + fast_jsonapi/attribute.rb:14:in 'serialize' (Datadog death_handler 로그). progress_serializer.rb:91attribute :record_completed_at, &:record_complete_at 코드. fast_jsonapi attribute.rb:14arity.abs == 1 ? 1-arg : 2-arg 분기. as_indexed_jsonserialization_params를 비어있지 않게 넘김 (searchable/progress.rb:232) Confirmed
H2 record_complete_at 메서드 시그니처가 최근 변경되어 인자 1개를 받도록 바뀌었다 없음 progress.rb:91-93은 0-인자 정의이며 git log에서 최근 시그니처 변경이 없음. 동일 코드가 2023-04-11부터 존재 Rejected
H3 Facility Transfer 코드가 직접 record_complete_at(arg)를 호출했다 없음 스택트레이스가 fast_jsonapi 직렬화 경로에서 시작되며 Transfer 코드 프레임이 호출 stack에 없음 Rejected
H4 Elasticsearch 응답 오류로 인한 우연한 메시지 일치 없음 Index error 메시지가 Cupix::Logger.error("Index error - #{e.message}", ...) (searchable.rb:51)에서 만들어진 것이며, 같은 request_id의 death_handler 로그에 정확한 ArgumentError 스택이 잡힘 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: app/serializers/progress_serializer.rb:91
  • 방향: &:record_complete_at 단축 표기를 fast_jsonapi가 0-인자 호출로 분기되도록 1-인자 람다나 명시적 block 형태로 교체한다. 동일한 직렬화 결과를 유지하되 fast_jsonapi의 arity 분기에서 arity.abs == 1 경로를 타도록 만드는 것이 핵심이다 (예: 1-인자 람다 또는 attribute(:record_completed_at) { |progress| progress.record_complete_at } 형태).
  • 근거: fast_jsonapi 1.5 Attribute#serialize 가 Proc의 arity로 호출 인자 수를 결정한다. Symbol#to_proc이 만드는 Proc의 arity는 -2이므로 항상 2-인자 분기를 타게 되며, 이는 Properties::Progress#record_complete_at의 0-인자 시그니처와 충돌한다.
  • 블록을 그냥 제거하면 안 되는 이유: serializer 키(record_completed_at, "d" 포함)와 모델 메서드명(record_complete_at, "d" 없음)이 다르다. attribute :record_completed_at만 작성하면 fast_jsonapi object_serializer.rb:191-195method: :record_completed_at을 등록하고, attribute.rb:13-17record.public_send(:record_completed_at) 분기를 타서 Progress에 존재하지 않는 메서드를 호출한다 (NoMethodError). 다시 말해 &:record_complete_at 부분은 키-메서드 이름 매핑을 담당하므로 형태만 바꿔야지(do |progress| progress.record_complete_at end 또는 1-인자 람다) 삭제해서는 안 된다. 같은 파일의 record_undetected_at/record_incomplete_at/record_unset_at (line 92-94)은 키와 메서드명이 일치해서 블록 없이 동작하지만, record_completed_atrecord_complete_at은 그렇지 않다.
  • 검증: ProgressSerializer 단위 spec에서 serializable_hash를 호출했을 때 record_completed_at 키가 예외 없이 반환되는지 확인. 실 데이터에 대해 as_indexed_json 호출이 성공하는지 통합 spec 또는 콘솔 검증.

단기 개선 (1주 이내)#

  • 다른 &:-스타일 attribute 점검 (Revision 3에서 실제 검사 완료): app/serializers/ 전반의 attribute :foo, &:bar 패턴 일괄 조사 결과 다음 두 곳에 동일 패턴 위험이 추가로 존재한다. 자세한 분류는 아래 "## Other Serializers Audit" 섹션 참고.
    • app/serializers/category_serializer.rb:26attribute :row_order, &:id (ActiveRecord::Base#id는 0-인자, 동일 ArgumentError 가능성).
    • app/serializers/schedule_revision_serializer.rb:35attribute :object_key, &:object_key (ScheduleRevision#object_key(ver = nil)은 1 optional 인자라 예외는 없지만 serialization_params Hash가 ver로 전달되어 잘못된 object_key 문자열을 만든다).
    • 그 외 &:_user, &:_team, &:_facility, &:_parent, &:_billable 등 underscore-prefixed 메서드들은 app/models/application_record.rb:84app/models/concerns/cachable.rb:7에서 |*args, **kwargs, &block| 시그니처로 자동 정의되어 가변 인자를 받으므로 안전.
  • _update_document 실패 재시도 루프 차단: 현재 searchable.rb:118-120StandardError 전반을 BulkIndexWorker로 재큐잉한다. ArgumentError처럼 입력 변화가 없으면 재시도해도 100% 실패가 보장되는 예외는 즉시 실패로 분류해 dead 큐 누적을 줄이고 알람 신호를 명확히 하도록 분기 추가를 검토.
  • Facility Transfer 워커 보호: Transfer 작업 도중 단일 Progress 레코드 인덱싱 실패가 transfer 자체를 in error. 상태로 만드는지 여부를 확인하고, indexing 실패와 transfer 진행을 분리하는 방향 검토.

장기 개선 (재발 방지)#

  • fast_jsonapi 사용 가이드라인 정립: &:method 단축 표기 대신 attribute(:key) { |record| record.method } 또는 1-인자 람다를 권장 표준으로 채택. 직렬화기 lint 룰(예: 커스텀 RuboCop cop 또는 grep 기반 CI 체크)로 &: 패턴 자체를 금지하거나 화이트리스트화.
  • fast_jsonapi 1.5는 2018년 이후 유지되지 않는 버전이다. 활성 메인테이너 fork (예: jsonapi-serializer)로의 마이그레이션을 장기 로드맵에 추가.

Monitoring#

  • 추가할 알림:
    • service:cupixworks-worker @class:Progress @function:_update_document "wrong number of arguments" 발생 시 즉시 Slack 알람.
    • service:cupixworks-worker "Sidekiq job died after all retries" @error.msg:"wrong number of arguments" 알람.
  • 추가할 메트릭/대시보드:
    • BulkIndexWorker re-enqueue 빈도 (단위 시간당 build-up 모니터링).
    • Facility Transfer 작업 종료 시 인덱싱 실패 건수.

Datadog 쿼리 예시:

text
service:cupixworks-worker status:error @class:Progress "wrong number of arguments"
text
service:cupixworks-worker "[Facility Transfer]" status:error

Other Serializers Audit#

Revision 3 피드백 ("여기말고 다른 serializer 에서도 동일한 문제가 있는지 확인")에 따라 tesla/app/serializers/ 전체를 조사했다. 총 230여 개의 attribute :foo, &:bar 사용처를 분류한 결과는 아래와 같다.

분류 기준#

  • Vulnerable (확실): 대상 메서드가 0-인자로 정의되어 있어 serialization_params가 인자로 전달되면 ArgumentError가 확정적으로 발생.
  • Latent / silent bug: 대상 메서드가 1+ optional 인자를 받아 예외는 발생하지 않지만 serialization_params Hash를 의도하지 않은 인자로 받아 잘못된 결과를 산출.
  • Safe (variadic): 대상 메서드가 *args, **kwargs 시그니처(직접 정의 또는 메타프로그래밍)라 추가 인자를 무시하고 정상 동작.

Findings#

# 위치 패턴 대상 메서드 정의 상태
1 app/serializers/progress_serializer.rb:91 attribute :record_completed_at, &:record_complete_at app/models/concerns/properties/progress.rb:91-93 def record_complete_at (0-인자) Vulnerable (확정 - 본 인시던트의 원인)
2 app/serializers/category_serializer.rb:26 attribute :row_order, &:id ActiveRecord::Base#id — Rails 7 attribute reader (0-인자) Vulnerable (잠재) — Category 인덱싱 시 searchable/category.rb:83-104as_indexed_jsonrow_order가 fieldset에 포함되지 않아 평소엔 발현되지 않음. 그러나 API endpoint(api/v1/categories_controller.rb:46-67)는 @fields = params[:fields]로 fieldset을 클라이언트가 지정하게 하므로 ?fields[category]=row_order로 호출되면 즉시 ArgumentError. Datadog 14일 retention 내에서는 발현 사례 없음(service:cupixworks-api "wrong number of arguments (given 1, expected 0)" 검색 결과 0건).
3 app/serializers/schedule_revision_serializer.rb:35 attribute :object_key, &:object_key app/models/schedule_revision.rb:25 def object_key(ver = nil) — 1 optional 인자 Latent silent bugrecord.object_key(serialization_params) 호출 시 ver = {} (또는 사용자 params)로 평가되어 line 26의 ver ? "_r#{ver}" : "_r#{revision}"이 truthy 분기를 타고 "_r{}" 등 잘못된 revision suffix가 만들어짐. 예외 없이 as_indexed_json(searchable/schedule_revision.rb:69) / Firebase sync(firebase/schedule_revision.rb:27) 양쪽에서 잘못된 object_key 필드가 인덱스에 저장될 수 있다. Datadog로는 검출 불가(예외 없음).
4 app/serializers/permitted_accessor_serializer.rb:4 attribute :type, &:associated_resourcable_type app/models/association.rb:9 belongs_to :associated_resourcable, polymorphic: true의 _type 컬럼 — Rails attribute reader (0-인자 추정) Potentially vulnerable, 미발현controllers/concerns/permitted_accessor_controller.rb는 fieldset filtering 없이 PermittedAccessorSerializer를 호출하므로 :type attribute는 항상 직렬화 경로를 탄다. 이론적으로 ArgumentError가 나야 하지만 Datadog 14일 retention 동안 0건. AR 자동 생성 attribute method가 일부 경로에서 가변 인자를 묵시적으로 수용하거나, 이 controller endpoint 호출 빈도가 매우 낮아 노출되지 않은 것으로 추정. 확정 판정에는 production console에서 record.associated_resourcable_type({})을 실행하는 검증이 필요.
5 (다수) app/serializers/*_serializer.rb&:_user, &:_team, &:_facility, &:_workspace, &:_record, &:_level, &:_bim, &:_review, &:_parent, &:_billable, &:_capture, &:_default_workflow 등 underscore-prefixed 모든 패턴 (~210건) attribute :user, &:_user app/models/application_record.rb:84 `define_method "_#{name}" do *args, **kwargs, &block

Datadog Cross-Check#

같은 ArgumentError 패턴을 14일 retention 전체에서 조사:

text
service:(cupixworks-api OR cupixworks-worker) "wrong number of arguments (given 1, expected 0)"
  • 결과: 42건. 모두 (a) Progress._update_document / Progress._index_document (본 인시던트, 39건) 또는 (b) Cupix::PubSub::Subscribers::ElementRecordSynchronizer#created(lib/cupix/pub_sub/subscribers/element_record_synchronizer.rb:5-7, 3건)에서 발생. 후자는 serializer &: 패턴과 무관def created가 0-인자로 정의되었는데 subscriber base가 event 인자로 호출하기 때문에 발생한 별도 버그(같은 root cause class: 0-arity 메서드에 1 인자 전달).
  • 따라서 현재 production traffic 기준으로 serializer &: 패턴 중 실제 외부에서 발현된 것은 progress_serializer.rb:91 한 군데뿐이지만, category_serializer.rb:26permitted_accessor_serializer.rb:4는 잠재적 ticking bomb이며, schedule_revision_serializer.rb:35는 소리 없이 잘못된 데이터를 인덱스에 쓰고 있을 수 있다.

권장 후속 조치#

  1. 즉시: progress_serializer.rb:91 fix와 함께 category_serializer.rb:26, schedule_revision_serializer.rb:35, permitted_accessor_serializer.rb:4도 같은 PR에서 attribute(:key) { |record| record.method } 형태(또는 1-인자 람다)로 함께 교체.
  2. CI lint: app/serializers/**/*.rb에 대해 attribute\s+:\w+,\s*&: 정규식을 grep하는 RuboCop cop 또는 단순 grep CI step을 추가하여 향후 재도입을 차단.
  3. 검증 (production console 또는 spec): PermittedAccessorSerializer 의 :type이 0-인자 호출에 실제로 raise하는지 확인하여 위 표의 "potentially vulnerable" 분류를 확정/철회.

Risk Assessment#

  • Risk level: medium
  • 예상 복잡도: trivial (한 줄 수정)
  • 영향: 수정 자체는 단순하나 fast_jsonapi의 &: 패턴이 다른 serializer에도 존재할 가능성이 있어 동일 버그가 잠재. 검색 인덱스 stale 가능성으로 인해 사용자 가시 지표(검색·집계)에 영향.

Revision History#

Revision 2#

Feedback: attribute :record_completed_at, &:record_complete_at 에서 뒷부분(, &:record_complete_at)을 아예 제거해도 동작하는지 확인 요청.

판정:

피드백 항목 판정 근거
&:record_complete_at 부분을 삭제해서 attribute :record_completed_at만 남겨도 되는지 검토 거부 (삭제 불가) serializer 키와 모델 메서드 이름이 다르다. 모델 메서드는 record_complete_at (no "d"); app/models/concerns/properties/progress.rb:91-99. serializer 키는 record_completed_at (with "d"); app/serializers/progress_serializer.rb:91, app/models/concerns/searchable/progress.rb:181,265. fast_jsonapi object_serializer.rb:188-196은 블록과 추가 메서드명이 모두 없으면 method: attr_name을 사용하므로 :record_completed_at이 등록된다. 그 다음 attribute.rb:13-17method.is_a?(Proc)가 false인 경우 record.public_send(method)를 호출 — 즉 progress.public_send(:record_completed_at)이 호출되는데, 이런 메서드는 Progress에 존재하지 않으므로 NoMethodError가 발생한다. 같은 파일의 record_undetected_at/record_incomplete_at/record_unset_at(line 92-94)은 모델 메서드명과 정확히 일치하기 때문에 블록 없이 정상 동작하지만, record_completed_atrecord_complete_at은 비대칭이므로 매핑 정보가 반드시 필요하다.

변경 사항:

  • ## Fix Recommendation > 즉시 조치 (Critical) 섹션에 "블록을 그냥 제거하면 안 되는 이유" 항목을 추가하여, 키와 메서드명 비대칭 때문에 블록 자체를 삭제할 수 없고 형태만(블록/람다로) 바꿔야 한다는 점을 명시.

추가 조사 내용:

  • tesla/vendor/cache/fast_jsonapi-1.5.gem을 추출해 lib/fast_jsonapi/object_serializer.rb attributes 매크로(line 183-199) 구현 확인 — 블록과 명시적 method_name 모두 없을 때 method: method_name(Symbol)이 등록됨.
  • lib/fast_jsonapi/attribute.rb:11-19 serialize에서 method가 Symbol인 경우 record.public_send(method)로 호출됨을 재확인.
  • tesla 레포에서 record_completed_at 사용처 grep — 정의된 인스턴스 메서드는 없고, ES 인덱스 필드 이름과 serializer fields 리스트에만 등장하는 것으로 확인.

Revision 3#

Feedback: "여기말고 다른 serializer 에서도 동일한 문제가 있는지 확인" — progress_serializer.rb:91 외 다른 serializer에서 같은 &:method_name arity 버그가 있는지 전수 조사 요청.

판정:

피드백 항목 판정 근거
다른 serializer에 동일한 &: arity 버그가 있는지 전수 조사 수용 tesla/app/serializers/ 전체에서 attribute :foo, &:bar 패턴 ~230건을 grep으로 수집 후 각 대상 메서드 정의를 확인. 결과: (1) progress_serializer.rb:91 외에 category_serializer.rb:26 attribute :row_order, &:id (AR::Base#id 0-인자, fieldset에 row_order 포함 시 ArgumentError), schedule_revision_serializer.rb:35 attribute :object_key, &:object_key (schedule_revision.rb:25 def object_key(ver = nil) 1 optional 인자 — 예외는 없으나 serialization_params Hash가 ver로 전달되어 잘못된 string 생성), permitted_accessor_serializer.rb:4 attribute :type, &:associated_resourcable_type (association.rb:9의 polymorphic type 컬럼, AR 자동 생성 reader는 0-인자 추정 — 이론상 vulnerable이나 Datadog 14일 동안 0건 발현). (2) 그 외 ~210건의 &:_user/&:_team/&:_facility/&:_parent/&:_billable 등 underscore-prefixed 패턴은 모두 app/models/application_record.rb:84 (overridden belongs_to) 및 app/models/concerns/cachable.rb:7 (Cachable concern)에서 `define_method "#{name}" do

변경 사항:

  • ## Fix Recommendation > 단기 개선 (1주 이내)의 "다른 &:-스타일 attribute 점검" 항목을 실제 조사 결과로 교체. 발견한 3건의 후보 위치를 명시.
  • ## Risk Assessment 섹션 직전에 새 섹션 ## Other Serializers Audit 추가 — 분류 기준, findings 표(5개 row), Datadog cross-check 결과, 권장 후속 조치 포함.

추가 조사 내용:

  • tesla/app/serializers/**/*.rb 전체 grep attribute\s+:\w+\s*,\s*&:\w+ 으로 ~230건 수집.
  • tesla/app/models/application_record.rb:81-95 의 overridden belongs_to 매크로에서 define_method "_#{name}" do |*args, **kwargs, &block| 확인 — 모든 _<assoc> 메서드가 가변 인자.
  • tesla/app/models/concerns/cachable.rb:7 define_method('_parent') do |*args, **kwargs, &block| 확인.
  • tesla/app/models/schedule_revision.rb:25 def object_key(ver = nil) 시그니처 확인 — silent latent bug.
  • tesla/app/controllers/api/v1/categories_controller.rb:46-67 에서 @fields = params[:fields] 동적 fieldset 확인 — row_order 요청 시 trigger 가능.
  • tesla/app/models/concerns/searchable/category.rb:83-104as_indexed_json 에서 row_order가 fieldset에 포함되지 않음 확인 — 인덱싱 경로는 안전.
  • tesla/app/controllers/concerns/permitted_accessor_controller.rb 에서 fieldset filtering 없이 PermittedAccessorSerializer 호출 확인 — :type 항상 직렬화.
  • Datadog 14일 retention 전수 검색: service:(cupixworks-api OR cupixworks-worker) "wrong number of arguments (given 1, expected 0)" 결과 42건, 모두 Progress 또는 ElementRecordSynchronizer(serializer 무관).