ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'users.ispring_user_id' in 'field list'
RCA: ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'users.ispring_user_id' in 'field list'
Overview#
What Happened#
cupixworks-api (tesla) 에서 SELECT ... users.ispring_user_id ... 쿼리가 이미 삭제된 컬럼을 참조해 Mysql2::Error: Unknown column 'users.ispring_user_id' in 'field list' 를 발생시켰다. users.ispring_user_id 컬럼은 2025-11-12 커밋 89883f361 (TSLA-10903, iSpring user 테이블 분리)에서 remove_column :users, :ispring_user_id 마이그레이션으로 삭제되고 별도 ispring_users 테이블로 옮겨졌다. 현재 코드베이스(master/develop)는 이 컬럼을 직접 SELECT 하지 않으며(User has_one :ispring_user association 사용), 에러는 deploy 시점의 schema/code 불일치로만 발생한다. Datadog ET 의 regression 필드가 2025-12-01 resolved → 2026-02-03 regressed 를 보여주고, last_seen version 이 production-...20260806t0214z0 배포판이라는 점이 이를 뒷받침한다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | ActiveRecord::StatementInvalid |
| exception.message | Mysql2::Error: Unknown column 'users.ispring_user_id' in 'field list' |
| top_frame | vendor/bundle/ruby/3.3.0/gems/mysql2-0.5.4/lib/mysql2/client.rb (_query) |
| runtime | Ruby 3.3.0, Rails 7.2, mysql2 0.5.4 |
| deploy (first_seen) | qa-us-west-2-20251114t0816z0-d37346b4-cupixworks |
| deploy (last_seen) | production-us-west-2-20260806t0214z0-c1c2bbd4-cupixworks |
| is_crash | false |
| env | QA + production (us-west-2) |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| cupixworks-api (tesla) | 710 (ET 누적) | 배포 cutover 중 일부 User 관련 쿼리 실패. is_crash:false, 프로세스 drain 후 자가 해소 |
영향은 배포 전환(blue/green cutover) 시간창에 국한된다. 정상 운영 시간대에는 발생하지 않는다.
Timeline#
- 2025-11-05 13:13 KST — 커밋
b32de125b(TSLA-10903) User iSpring 연동,users.ispring_user_id컬럼 도입 (migration20251104070045). - 2025-11-12 08:48 KST — 커밋
89883f361(TSLA-10903) iSpring user 테이블 분리.remove_column :users, :ispring_user_id(migration20251112084437) +has_one :ispring_userassociation 전환. - 2025-11-14 15:37 KST — 커밋
59c393e54(TSLA-10903) class scope error 수정. 이 배포판(qa-...20251114t0816z0)이 ET first_seen version. - 2025-11-17 11:25 KST — 최초 발생(ET first_seen).
- 2025-12-01 16:39 KST — ET regression 상 resolved.
- 2026-02-03 13:17 KST — regressed (
qa-...20260203t0144z0). - 2026-08-06 14:51 KST — 최근 발생(ET last_seen,
production-...20260806t0214z0배포판). 동일 배포 시간창(05:42-05:53 UTC)에43464ba9service_jwt 회귀와 함께 status-board 인시던트2026-08-06-svc-cupixworks-api--unknown-1로 묶임.
Error Log#
Mysql2::Error: Unknown column 'users.ispring_user_id' in 'field list'
Impact#
- Service:
cupixworks-api - 발생 횟수: 710 (ET 누적)
- 최초 발생: 2025-11-17 11:25 KST
- 최근 발생: 2026-08-06 14:51 KST
Root Cause Summary#
users.ispring_user_id 컬럼은 2025-11-12 (89883f361, migration 20251112084437)에서 삭제되어 ispring_users 테이블로 분리되었다. 현재 코드는 이 컬럼을 직접 SELECT 하지 않는다 — User 모델은 has_one :ispring_user (app/models/user.rb:37) association 을 사용하고, ispring 관련 코드는 모두 ispring_user.ispring_user_id 형태로 별도 테이블을 조회한다. 그럼에도 에러가 계속 발생하는 이유는 blue/green(EB color) 배포 cutover 시점의 schema cache/code 불일치다. 삭제 마이그레이션이 DB 에 적용된 시점에도, 마이그레이션 이전 스키마를 캐시한 old-color 앱 프로세스가 여전히 트래픽을 처리하면서 SELECT users.*(캐시된 컬럼 목록 기준으로 users.ispring_user_id 포함)를 발행 → 이미 없는 컬럼 참조로 Mysql2::Error 를 낸다. old-color 프로세스가 drain 되면 자동 해소되며(is_crash:false), ET 의 resolved(2025-12-01)→regressed(2026-02-03) 사이클과 매 배포판별 재발이 이 메커니즘을 확증한다. 이는 서버 코드 결함이 아니라 destructive migration(컬럼 삭제)을 old 코드 drain 이전에 적용하는 배포 순서(process) 문제다.
Technical Analysis#
Code Path#
- Column 도입: migration
20251104070045_add_i_spring_info_on_user_and_team.rb - Column 삭제(root cause 트리거): migration
20251112084437_remove_ispring_columns_from_users.rb - Failure point: mysql2 어댑터
_query(컬럼 없는 SELECT 실행)
컬럼을 삭제한 마이그레이션:
class RemoveIspringColumnsFromUsers < ActiveRecord::Migration[7.2]
def change
remove_column :users, :ispring_user_id, :string
remove_column :users, :ispring_joined_at, :datetime
end
end
현재 스키마는 ispring_user_id 를 users 가 아니라 ispring_users 테이블에 둔다:
create_table "ispring_users", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "ispring_joined_at"
t.string "ispring_user_id"
t.datetime "updated_at", null: false
t.bigint "user_id", null: false
t.index ["ispring_user_id"], name: "index_ispring_users_on_ispring_user_id"
t.index ["user_id"], name: "index_ispring_users_on_user_id", unique: true
end
현재 코드는 컬럼을 직접 참조하지 않고 association 으로만 접근한다:
has_one :ispring_user, dependent: :destroy
existing_user = User.joins(:ispring_user).where(email: user.email).first
# ...
user.create_ispring_user!(
ispring_user_id: response,
ispring_joined_at: Time.current
)
기대 동작: 마이그레이션이 컬럼을 삭제한 뒤에는 어떤 코드도 users.ispring_user_id 를 SELECT 하지 않아야 한다. 실제 동작: 삭제 마이그레이션 적용 직후, 마이그레이션 이전 스키마를 로드한 old-color 프로세스가 캐시된 컬럼 목록으로 SELECT ... users.ispring_user_id ... 를 발행 → Unknown column. app/, serializers/, repositories/ 어디에도 select(:ispring_user_id) 같은 명시적 SELECT 는 존재하지 않으므로(grep 확인) SQL 은 users.* 확장 또는 캐시된 attribute 목록에서만 나올 수 있다.
Log Evidence#
Datadog Error Tracking issue 원본(getIssue acdd2caa)이 가장 신뢰할 수 있는 근거였다. 로그/스팬 텍스트 검색은 전부 0건이었다(rescue/handled + retention 밖 배포 시간창).
사용한 쿼리(모두 now-14d, 0건):
service:cupixworks-api "ispring_user_id"
service:cupixworks-api "users.ispring_user_id"
service:cupixworks-mysql2 "ispring_user_id"
service:cupixworks-api status:error "Unknown column"
service:cupixworks-mysql2 status:error "Unknown column"
ET issue 원본 attributes (deploy 시간창 재발 + regression 사이클 확증):
{
"error_message": "Mysql2::Error: Unknown column 'users.ispring_user_id' in 'field list'",
"error_type": "ActiveRecord::StatementInvalid",
"file_path": "/var/app/current/vendor/bundle/ruby/3.3.0/gems/mysql2-0.5.4/lib/mysql2/client.rb",
"function_name": "_query",
"is_crash": false,
"first_seen_version": "qa-us-west-2-20251114t0816z0-d37346b4-cupixworks",
"last_seen_version": "production-us-west-2-20260806t0214z0-c1c2bbd4-cupixworks",
"regression": {
"resolved_at": "2025-12-01T07:39:49.672088Z",
"regressed_at": "2026-02-03T04:17:36.148Z",
"regressed_at_version": "qa-us-west-2-20260203t0144z0-f4339093-cupixworks"
},
"service": "cupixworks-api",
"state": "OPEN"
}
배포 구조 근거: tesla 는 Elastic Beanstalk blue/green(color) 배포를 쓴다 — .platform/confighooks/postdeploy/02_restore_on_color_match.sh, 03_cleanup_on_color_mismatch.sh 가 color 전환을 처리한다. 마이그레이션은 별도 단계(migration sidekiq / init)에서 적용되어 old-color 앱과 시간창이 겹칠 수 있다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Destructive migration(컬럼 삭제)이 old-color 프로세스 drain 이전에 적용되어, 배포 cutover 시간창에 stale schema cache 로 SELECT users.ispring_user_id 발행 |
migration 20251112084437 이 컬럼 삭제; ET regression resolved(2025-12-01)→regressed(2026-02-03); 매 배포판별 재발(first_seen_version qa-20251114, last_seen_version production-20260806); is_crash:false(rescue/자가치유); tesla EB color 배포(.platform/confighooks/postdeploy/02_restore_on_color_match.sh) |
— | Confirmed |
| H2 | 현재 코드에 users.ispring_user_id 를 직접 SELECT 하는 결함이 존재 |
에러 메시지가 field list(명시적 SELECT) |
app/ 전체 grep 결과 select(:ispring_user_id)/serializer/repository 참조 0건; User has_one :ispring_user + ispring_user.ispring_user_id 만 사용; ispring 코드 2025-11-14 이후 변경 없음 |
Rejected |
| H3 | Representative 가 stale — 실제 현재 발생은 다른 컬럼(spacetimes.summary_text 등) 변형 |
같은 05:xx UTC 시간창에 spacetimes.summary_text 버스트(clusters 5e254667/a89a3762) 존재 |
ET issue acdd2caa 는 spacetimes.summary_text 와 별개 ET issue (각 e t_issue_id 상이); getIssue error_message 가 users.ispring_user_id 로 확정; 다른 컬럼은 별도 issue 로 집계됨 |
Rejected |
| H4 | 진짜 코드/데이터 버그로 유저 요청이 지속 실패(crash) | 710 누적 발생 | is_crash:false; now-14d 로그/스팬 0건(정상 운영 시간대 미발생); 발생이 배포판(version) 경계에 집중 |
Rejected |
Fix Recommendation#
이 클러스터는 서버 코드 결함이 아니라 destructive migration 의 배포 순서(process) 문제다. 현재 코드는 이미 올바르다(association 사용). 코드 변경으로 수정할 대상이 없다.
즉시 조치 (Critical)#
- 코드 변경 불필요.
errors/클러스터는 noise 로 분류하고 ET issueacdd2caa를 IGNORE 처리 권장. - 이번 발생은
production-...20260806t0214z0배포 cutover 시간창의 transient 이므로, old-color 프로세스 drain 완료 후 재발 여부만 모니터링.
단기 개선 (1주 이내)#
- 배포 파이프라인에서 destructive migration(컬럼/테이블 삭제)을 앱 old-color drain 이후로 분리하는 2단계 배포(expand/contract) 규칙 도입 검토.
remove_column류는 신규 코드가 컬럼을 더 이상 참조하지 않게 된 다음 배포에서만 적용한다. - 이미 삭제된 컬럼을 참조하는 SQL 이 어디서 확장되는지(Prepared statement/schema cache) 확인이 필요하면, 배포 시
bin/rails db:schema:cache:clear또는 앱 부팅 시 schema cache 재생성이 보장되는지 점검. (uncertain -- needs verification: tesla 는 committedschema_cache.yml이 없고 명시적 schema-cache 설정도 없음)
장기 개선 (재발 방지)#
- migration 계열 ET issue(
Unknown column ... in 'field list')를 배포 cutover noise 로 분류하는 alerting 규칙.is_crash:false+ 발생이 특정deploy version경계에 집중되면 자동으로 배포-transient 로 태깅. - Expand/contract(parallel change) 마이그레이션 정책을 CI/리뷰 체크리스트에 반영해 컬럼 삭제 PR 이 old 코드 참조 제거 배포와 최소 1 릴리즈 간격을 두도록 강제.
Monitoring#
배포 후 재발 확인 (deploy cutover 시간창에만 뜨는지 검증):
service:cupixworks-api "Unknown column 'users.ispring_user_id'"
mysql2 어댑터 스팬에서 handled 여부 확인:
service:cupixworks-mysql2 status:error "ispring_user_id"
배포판(version) 경계와의 상관 확인 — 특정 배포 직후에만 count 가 튀는지:
service:cupixworks-api "Unknown column" "users.ispring_user_id"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (코드 변경 없음; 배포 프로세스/알림 조정만)
Noise Verdict#
noise — 삭제된 users.ispring_user_id 컬럼을 참조하는 SQL 은 현재 코드에 없고 blue/green 배포 cutover 시점의 stale schema cache 로만 발생하는 transient 이며(is_crash:false, drain 후 자가 해소), 코드 수정 대상이 아니라 destructive migration 배포 순서 문제이므로 noise 로 분류한다.