같은 모듈 안에서 비슷한 일을 하는 클래스가 여럿일 때, 어느 하나에만 개선이 들어가고 나머지는 그대로 남는 일이 있습니다. 기능이 없는 것도 아니고 버그가 눈에 띄는 것도 아니라 한동안 지나가기 쉽습니다. 이번에 LangChain4j에 올려 머지된 변경이 그런 자리였습니다. Jlama 모듈에서 모델 다운로드가 실패할 때, 채팅 모델은 인증 오류인지 모델을 못 찾은 것인지 알려 주는데 나머지 네 개는 뭉뚱그린 예외만 던지고 있었습니다.예외 타입이 뭉개지던 경로Jlama는 자바에서 모델을 로컬로 내려받아 돌리는 라이브러리이고, LangChain4j의 langchain4j-jlama 모듈이 이를 감싸 줍니다. 모델 객체를 만들 때 필요한 모델 파일을 먼저 내려받는데, 이 다운로드가 실패하면 그 실패를 어떤 ..
주소 끝의 슬래시 하나는 평소에 신경 쓸 일이 없습니다. 그런데 라이브러리가 그 주소를 다른 라이브러리에 넘겨주는 자리에서는 그 한 글자가 예외로 이어지기도 합니다. 이번에 LangChain4j에 올려 머지된 변경이 그런 경우였습니다. HuggingFace 모듈에 직접 엔드포인트 주소를 지정하면, 그 주소가 슬래시로 끝나지 않았다는 이유만으로 모델 객체를 만드는 순간 예외가 났습니다. 커스텀 주소를 넣으면 만들어지지도 않던 클라이언트당시 DefaultHuggingFaceClient는 Retrofit으로 HTTP 호출을 처리하고 있었습니다. Retrofit은 기준 주소를 받을 때 반드시 /로 끝나야 한다는 규칙을 두고, 그렇지 않으면 baseUrl must end in /라는 메시지와 함께 IllegalA..
임베딩 스토어를 쓰다 보면 메타데이터로 결과를 좁히는 일이 자주 있습니다. 문서에 붙여 둔 키-값 중에서 특정 값을 가진 것만 골라내는 흔한 작업입니다. LangChain4j는 이런 조건을 Filter라는 공통 타입으로 표현해 두고, 각 스토어 모듈이 그것을 자기 데이터베이스가 알아듣는 질의로 번역합니다. 사용하는 쪽에서는 어떤 스토어를 쓰든 같은 코드를 쓰면 되니 편한 구조입니다. 그런데 이 "번역"이라는 말 안에는 조용한 위험이 하나 들어 있습니다. 번역이 조금이라도 어긋나면, 같은 필터를 걸었는데 스토어마다 다른 결과가 나옵니다. 그것도 예외가 터지는 게 아니라 그냥 결과가 조금 달라지는 방식으로요. 이번에 LangChain4j에 올려 머지된 변경은 정확히 그 종류의 어긋남을 다룬 것이었습니다. l..
설정 문서 생성기가 &를 그냥 흘려보내고 있었습니다Apache Paimon은 설정 옵션 문서를 손으로 쓰지 않고 코드에서 생성합니다. paimon-docs 모듈의 Utils.escapeCharacters가 옵션 설명 문자열을 HTML에 안전하게 넣을 수 있도록 특수 문자를 이스케이프하는 역할을 합니다. 이 메서드가 앰퍼샌드(&)를 처리하지 않고 있어, 이번 글에서는 그것을 채운 apache/paimon#8552를 이야기하려 합니다. 기존 구현은 꺾쇠괄호만 이스케이프하고 있었습니다.// AS-ISpublic static String escapeCharacters(String value) { return value.replaceAll("", TEMPORARY_PLACEHOLDER) ..
byte[] 키를 자바 Map으로 찾으면 어긋납니다Apache Paimon의 paimon-common 모듈에는 내부 데이터 구조를 비교하고 해시하는 InternalRowUtils가 있습니다. 이 유틸리티가 MAP 타입을 비교하는 방식에 어긋난 부분이 있어, 이번 글에서는 그것을 고친 apache/paimon#8536을 이야기하려 합니다.문제는 맵의 키를 찾는 방식이었습니다. 기존 equals는 한쪽 맵의 키를 꺼내 다른 쪽 맵에서 contains와 get으로 찾았습니다. // AS-ISObject key = get(keyArray1, i, mapType.getKeyType());if (!map2.contains(key) || !equals(map1.get(key), map2.get(key),..
생성된 비교기가 부동소수점을 == 로 비교하고 있었습니다Apache Paimon은 레코드를 비교하는 코드를 실행 시점에 생성합니다. paimon-codegen 모듈의 EqualiserCodeGenerator가 필드 타입에 맞춰 RecordEqualiser의 equals 본문을 Scala 템플릿으로 찍어 내는 구조입니다. 이 생성된 비교기가 부동소수점 필드를 다루는 방식에 어긋난 부분이 있어, 이번 글에서는 그것을 맞춘 apache/paimon#8534를 이야기하려 합니다. 문제의 출발점은 원시 타입 필드를 비교하던 자리였습니다. 생성기는 정수나 부동소수점 같은 내부 원시 타입을 만나면 자바 == 연산으로 비교하는 코드를 찍어 냈습니다.// AS-IS — 내부 원시 타입은 모두 == 로 비교if (isIn..
하나만 빠진 오버라이드Apache Paimon의 타입 시스템에서 중첩 타입들은 서로 닮은 메서드 묶음을 함께 오버라이드합니다. ArrayType, MapType, VectorType, RowType은 모두 equalsIgnoreFieldId와 isPrunedFrom을 각자 구현하고 있었는데, 같은 계열인 MultisetType만 이 둘을 오버라이드하지 않고 있었습니다. 이번 글에서 이야기할 기여는 그 빠진 자리를 채운 apache/paimon#8519입니다. 이 두 메서드는 스키마를 비교할 때 쓰입니다. equalsIgnoreFieldId는 두 타입이 필드 id만 다를 뿐 구조는 같은지 판정하고, isPrunedFrom은 한 타입이 다른 타입에서 일부 필드를 덜어 낸(프루닝된) 형태인지 판정합니다. 스키..
같은 인자를 두 번 넣은 해시Apache Paimon의 paimon-api 모듈을 살펴보다가, equals와 hashCode가 서로 다른 필드를 바라보고 있는 자리를 발견했습니다. FunctionChange의 중첩 타입인 UpdateDefinition에서 hashCode()가 이렇게 되어 있었습니다.@Overridepublic int hashCode() { return Objects.hash(definition, definition);}definition을 두 번 넣고, name은 빠져 있었습니다. 이번 글에서 이야기할 기여는 이 한 줄을 바로잡은 apache/paimon#8518입니다.FunctionChange는 함수 정의의 변경을 표현하는 타입으로, 정의를 추가하는 AddDefinition, 갱..
형제 타입은 이미 고쳐져 있었습니다Apache Paimon의 타입 시스템 코드를 읽다가, 서로 닮은 형제 클래스들 사이에서 한 곳만 다르게 처리된 부분을 발견했습니다. paimon-api 모듈의 VectorType.defaultSize()는 자식 크기를 곱할 때 오버플로를 막는 헬퍼를 쓰고 있었는데, 같은 계열의 RowType·MapType·MultisetType은 그냥 정수 덧셈으로 자식 크기를 합치고 있었습니다. 이 작은 비대칭이 이번 글에서 이야기할 기여의 출발점입니다. 실제로 올려 머지된 변경은 apache/paimon#8517입니다. defaultSize()는 각 데이터 타입이 메모리에서 차지하는 기본 바이트 크기를 어림하는 메서드입니다. 예를 들어 INT는 4, BIGINT는 8을 돌려주고, ..
PR #8492에서 제가 바꾼 것은 문자열 리터럴 한 글자입니다. s 하나를 지웠습니다. 이렇게 적어놓고 보면 굳이 글로 남길 일인가 싶지만, 이 한 글자를 확인하는 과정에서 저는 "테스트가 있다"와 "테스트가 검증한다"가 같은 말이 아니라는 것을 꽤 구체적으로 마주하게 됐습니다. 그 부분이 남아서 정리해둡니다. 클래스 이름과 다른 이름을 출력하던 메서드:exporters:otlp:profiles는 profiles 시그널을 OTLP gRPC로 내보내는 모듈입니다. profiles는 트레이스·메트릭·로그에 이어 OpenTelemetry에 들어온 비교적 새로운 시그널이고, 이 모듈은 alpha 산출물입니다. 이 모듈을 읽다가 OtlpGrpcProfileExporter의 toString()에서 멈췄습니다.@O..
OpenTelemetry Java 저장소에 몇 건의 변경을 올리면서, 저는 큰 기능을 만드는 것보다 이미 있는 코드가 스스로 약속한 것을 지키고 있는지 확인하는 쪽이 초보 기여자에게 더 맞는 일이라는 생각을 하게 됐습니다. 이번 글에서 다룰 PR #8489는 그런 종류의 변경입니다. 프로덕션 코드 네 줄과 테스트 파일 하나가 전부지만, 그 네 줄에 도달하기까지 확인해야 했던 것들이 저에게는 더 남았습니다. 엔드포인트를 검증한다는 말의 의미OpenTelemetry Java에서 exporter는 수집한 텔레메트리를 OTLP 같은 프로토콜로 백엔드에 보내는 역할을 합니다. 이때 어디로 보낼지를 지정하는 값이 엔드포인트고, 보통 http://localhost:4318 같은 문자열로 들어옵니다. exporters..
코드를 읽을 때 저는 대체로 메서드 하나를 붙잡고 그 안에서 값이 어떻게 흐르는지를 봅니다. 그 방식으로 찾을 수 있는 결함이 있고, 그 방식으로는 절대 안 걸리는 결함이 있습니다. 이 글에서 다룰 #17066은 후자였습니다. 관련된 메서드 두 개는 각각 놓고 보면 어디에도 이상이 없습니다. 문제는 둘을 순서대로 불렀을 때 생겼습니다. 어떻게 그 순서를 의심하게 됐는지, 그리고 한 줄을 고치기까지 무엇을 확인했는지를 정리해 보려고 합니다.셰딩된 객체가 필드를 기억하는 방식무대는 iceberg-core의 ShreddedObject(core/src/main/java/org/apache/iceberg/variants/ShreddedObject.java)입니다. Iceberg의 Variant는 스키마가 미리 정..
Apache Iceberg에 기여하면서 자주 쓰는 방법 중 하나는 같은 일을 하는 코드를 나란히 놓고 보는 것입니다. 다만 이 방법에는 함정이 있습니다. 구현이 서로 다르다는 사실만으로는 어느 쪽이 틀렸는지 알 수 없기 때문입니다. 이 글에서 다룰 #17055는 그 판단을 어디까지 밀어붙여야 했는지, 그리고 리뷰에서 받은 질문 하나가 제 근거를 어떻게 다시 확인하게 만들었는지에 관한 기록입니다. 저는 Iceberg의 벡터화 읽기 전반을 아는 사람이 아니라 이 한 경로를 코드로 따라가 본 사람의 시점에서 씁니다.딕셔너리 인코딩 값을 Arrow 벡터에 옮기는 자리문제가 있던 곳은 iceberg-arrow 모듈의 VectorizedDictionaryEncodedParquetValuesReader(arrow/s..
Apache Iceberg에 몇 건 기여하다 보니, 제가 코드에서 눈을 두는 자리가 조금씩 옮겨 갔습니다. 처음에는 값이 잘못 계산되는 곳을 찾았는데, 어느 순간부터는 "이 코드가 실패할 때 사용자에게 무엇이 보이는가"도 함께 보게 됐습니다. 이 글에서 다루는 #17098은 그렇게 찾은 건입니다. 고쳐도 정상적인 읽기·쓰기 동작은 단 하나도 바뀌지 않고, 오직 예외가 던져질 때의 메시지만 달라지는 수정입니다. 그런 변경을 왜 PR로 올릴 만하다고 판단했는지, 그리고 그 판단을 위해 무엇을 확인해야 했는지를 정리해 보려고 합니다. 저는 Iceberg 전반을 아는 사람이 아니라 이 한 경로를 코드로 확인해 본 사람의 시점에서 씁니다. 포맷 문자열이 포맷되지 않는 자리문제가 있던 곳은 ORC 스키마를 Iceb..
OpenTelemetry Java(open-telemetry/opentelemetry-java)에 기여를 이어 가면서, 같은 모듈 안에서 비슷한 역할을 하는 클래스들을 나란히 놓고 비교하는 습관이 생겼습니다. 이번 기여(#8490)도 그렇게 시작했습니다. 세 형제 exporter를 차례로 열어 보다가, 그중 하나만 클래스 설명이 통째로 비어 있는 것을 발견했습니다.같은 모듈, 같은 역할, 다른 문서문제의 자리는 :exporters:logging 모듈이었습니다. 이 모듈은 텔레메트리를 로그나 표준 출력으로 찍어 주는 exporter를 모아 둡니다. 수집기로 보내기 전에 콘솔이나 로그에서 무엇이 나가는지 확인하는, 거친 디버깅용 도구입니다. 여기에는 신호별로 세 exporter가 한 벌로 들어 있습니다. L..
OpenTelemetry Java(open-telemetry/opentelemetry-java)에 기여를 이어 가면서, 이번에는 평소 잘 들여다보지 않던 자리를 열어 보게 됐습니다. 바로 package-info.java입니다. 클래스도 메서드도 아닌, 패키지 전체를 한 문장으로 소개하는 문서 전용 파일입니다. 이번 기여(#8487)는 그 한 문장이 패키지가 실제로 담고 있는 것과 어긋나 있던 문제를 고친 기록입니다.패키지 소개가 두 가지만 말하고 있었다문제의 자리는 :exporters:logging-otlp 모듈의 패키지 설명이었습니다. 이 모듈은 텔레메트리를 OTLP JSON 형식으로 로거에 찍어 주는 exporter를 모아 둡니다. 그 패키지의 package-info.java는 이렇게 적혀 있었습니다..
오픈소스 저장소를 읽다 보면, 코드 자체보다 코드를 설명하는 문장이 먼저 눈에 걸릴 때가 있습니다. 이번에 OpenTelemetry Java(open-telemetry/opentelemetry-java)에 올린 두 번째 기여(#8484)가 그랬습니다. 선언형 설정(declarative config) API를 들여다보다가, 한 메서드의 Javadoc이 스스로와 어긋나 있는 것을 발견했습니다. 바뀐 코드는 문장 두 개뿐이지만, 왜 그런 어긋남이 생겼는지 따라가 보는 과정이 제게는 공부가 됐습니다.첫 문장과 @return이 다른 말을 하고 있었다문제의 자리는 :api:incubator 모듈의 DeclarativeConfigProperties 인터페이스였습니다. 이 인터페이스는 YAML로 작성한 설정을 프로그램..
오픈소스에 기여해 보고 싶다는 생각은 오래 했지만, 막상 시작은 늘 미뤘습니다. 큰 저장소를 열면 어디부터 봐야 할지 막막했고, 내가 고칠 만한 자리가 있을까 싶었습니다. 그러다 OpenTelemetry Java(open-telemetry/opentelemetry-java)를 골라 한 모듈씩 천천히 들여다보기로 했습니다. 모든 코드를 다 읽겠다는 욕심은 처음부터 버렸습니다. 대신 테스트 코드부터 읽었습니다. 테스트는 그 모듈이 무엇을 보장하려 하는지 가장 솔직하게 보여 주는 자리라고 생각했기 때문입니다. 이 글은 그렇게 찾은 작은 기여 한 건(#8483)을 정리한 기록입니다. 바뀐 코드는 단 한 줄이지만, 그 한 줄을 올리기까지 저장소의 구조와 규칙을 익히며 배운 것이 적지 않았습니다.형제 테스트가 서로..
Apache Iceberg에 몇 건 기여하면서 제가 가장 많이 바뀐 건 코드를 읽는 방식입니다. 처음에는 눈에 띄는 오타나 문서를 고치는 것으로 시작했는데, 회를 거듭할수록 "이 값이 저기까지 가서 어떻게 해석되는가"를 끝까지 따라가 보는 습관이 생겼습니다. 이 글에서는 그중 한 건, timestamp_ns 컬럼의 ORC 파일 통계가 잘못된 단위로 기록되던 버그를 고친 #16922를 중심으로, 어떻게 결함을 찾고 어디까지 확인한 뒤에야 고쳤는지를 정리해 보려고 합니다. 저는 Iceberg의 모든 영역을 아는 사람이 아니라, 이 한 경로를 코드와 공식 저장소로 확인해 본 사람의 시점에서 씁니다.문서 수정에서 코드 경로 추적으로첫 기여는 소소했습니다. 기여 문서의 예제 버전을 당시 지원 버전에 맞춰 갱신하거..
오픈소스 코드를 읽다 보면 가끔 "이건 분명히 의도와 다르게 쓰였다" 싶은 줄을 만납니다. #16924 (Spark 4.1: Throw on unsupported complex types in SparkValueConverter)가 그런 경우였습니다. 고친 분량은 키워드 하나, 정확히는 return을 throw로 바꾼 게 전부였습니다. 그런데 이 한 글자짜리 변경을 "안전하게 고쳤다"고 말하려면 생각보다 확인할 게 많았습니다. 이 글은 그 한 줄을 어떻게 찾고, 왜 문제인지 어떻게 따져 봤고, 영향이 어디까지인지를 어떻게 정직하게 적었는지 정리한 기록입니다. Apache Iceberg는 대규모 분석 테이블을 다루는 테이블 포맷이고, 이 저장소는 그 자바 구현입니다. 엔진 연동은 Spark, Flink처럼..
오픈소스에 기여한다고 하면 거창한 기능을 새로 만드는 모습을 떠올리기 쉽습니다. 저도 처음에는 그런 줄 알았습니다. 그런데 막상 Apache Iceberg 코드를 읽으면서 제가 할 수 있던 일은 대부분 "이미 있는 규칙을 한 군데만 빠뜨린 곳"을 찾아 메우는 작업이었습니다. 이 글은 그중 하나인 #16870 (Aliyun: Pass known file length through OSSFileIO.newInputFile)을 어떻게 찾고, 어떻게 고쳤고, 그 과정에서 Iceberg의 모듈 구조와 검증 방식을 어떻게 익혔는지 정리한 기록입니다. Apache Iceberg는 대규모 분석 테이블을 다루는 테이블 포맷이고, 이 저장소는 그 자바 구현입니다. 저는 클라우드와 데브옵스 쪽 일을 해 온 자바 개발자라서,..
오픈소스에 기여를 시작하기 전에는, 저장소의 코드가 대체로 옳게 짜여 있고 고칠 것은 어딘가 아주 어려운 자리에나 숨어 있을 거라고 막연히 생각했습니다. 그런데 막상 코드를 한 줄씩 읽어 보니 사정은 조금 달랐습니다. 정작 눈에 띄는 결함은 어려운 알고리즘이 아니라, 문자열 몇 개를 이어 붙이는 평범한 자리에 조용히 남아 있는 경우가 많았습니다. 이번에 LangChain4j에 올려 머지된 변경도 그런 종류였습니다. 코드 실행 결과를 화면에 보여 줄 마크다운 코드블록을 만드는 짧은 코드 한 토막이었는데, 그 결과물이 렌더러가 코드블록으로 인식하지 못하는 깨진 형태였습니다. 이 글은 그 자리를 찾고 고쳐서 올리기까지의 과정을 처음부터 정리한 기록입니다. Gemini의 코드 실행 결과가 텍스트로 바뀌는 자리L..
큰 기능을 새로 만드는 일만 기여라고 생각하던 시절이 있었습니다. 그런데 실제로 오픈소스 저장소를 들여다보기 시작하니, 정작 고칠 값어치가 있는 것은 눈에 잘 띄지 않는 곳에 조용히 숨어 있는 경우가 많았습니다. 이번에 LangChain4j에 올려 머지된 변경도 그런 종류였습니다. 코드 한 줄이 아니라 표현식 하나를 바꾸는 정도였지만, 그 한 곳을 찾기까지 읽어야 했던 맥락은 생각보다 넓었습니다. 이 글은 그 과정을 처음부터 되짚어 정리한 기록입니다.토큰 사용량이라는 눈에 잘 안 띄는 값LangChain4j는 여러 LLM 제공자를 자바에서 같은 방식으로 다룰 수 있게 감싸 주는 라이브러리입니다. OpenAI, Anthropic, Google 계열 등 제공자마다 응답 형식이 제각각인데, 라이브러리는 그것을..
시작은 "왜 이 답만 한 줄로 붙어 나오지?"라는 의문이었습니다로컬에서 도는 언어 모델을 쓰다 보면, 같은 질문에 같은 답을 받아도 어떤 때는 코드 블록과 목록이 멀쩡하게 나오고 어떤 때는 모든 줄이 한 줄로 뭉쳐 나오는 일을 겪습니다. 저는 LangChain4j의 로컬 추론 모델 쪽을 들여다보다가 이 현상을 만났고, 처음엔 모델 자체가 그렇게 생성한 줄 알았습니다. 그런데 같은 프롬프트를 조금 바꿔 다시 물으면 포맷이 살아 있었습니다. 모델이 아니라 그 아래에서 텍스트를 다듬는 코드가 범인일 수 있다는 생각이 들었습니다. 문제의 자리는 langchain4j-gpu-llama3 모듈의 GPULlama3ResponseParser였습니다. 이 클래스는 GPULlama3 계열 모델이 내놓는 응답을 받아, 모델..
오픈소스에 기여할 지점을 찾을 때, 저는 화려한 기능보다 "정상적인 입력인데 결과가 조금 이상한" 자리를 먼저 살피는 편입니다. 예외가 터지거나 로그에 붉은 줄이 남는 버그는 누군가 이미 신고했을 가능성이 크지만, 겉으로는 멀쩡하게 도는데 값만 미묘하게 어긋나는 종류는 오래 남아 있는 경우가 많기 때문입니다. 이번에 고친 것도 그런 부류였습니다. LangChain4j의 Elasticsearch 연동 모듈에서, 검색 점수가 사용자가 정한 임계값과 정확히 같을 때 그 결과가 조용히 사라지던 문제입니다. 변경 자체는 부등호 한 글자를 바꾼 것이 전부지만, 왜 그것이 버그인지를 확인하는 과정에서 배운 것이 많았습니다. 이 변경은 PR #5513으로 반영되었습니다. minScore가 RAG 검색에서 하는 일먼저 ..
오픈소스에 기여하겠다고 마음을 먹고 나서 한동안은 무엇을 고쳐야 할지가 가장 큰 벽이었습니다. 큰 기능을 새로 만드는 일은 엄두가 나지 않았고, 그렇다고 오타 수정만 반복하고 싶지도 않았습니다. 그래서 택한 방법은, 평소에 쓰거나 관심이 가는 라이브러리의 코드를 한 모듈씩 천천히 읽으면서, "이 코드는 이런 입력에서 어떻게 동작할까"를 스스로 되물어 보는 것이었습니다. LangChain4j는 자바로 LLM 애플리케이션을 만들 때 자주 쓰는 라이브러리라 익숙했고, 그중에서도 여러 에이전트가 서로 주장을 주고받는 토론(debate) 패턴을 담은 langchain4j-agentic-patterns 모듈이 눈에 들어왔습니다. 이번 글은 그 모듈에서 발견한 작은 버그 하나를 고쳐 머지되기까지의 과정을 정리한 것입..
지금까지 제가 다룬 기여 중에는 인자 순서가 바뀐 것도, 큰 정수가 좁혀진 것도, 목록에서 한 줄이 빠진 것도 있었습니다. 이번에 다룰 LangChain4j 수정은 그중에서도 가장 작습니다. 바뀐 것은 코드 한 곳의 글자 한 개입니다. 그런데 그 한 글자가, 흔히 쓰는 비디오 파일 하나가 제 짝을 못 찾고 엉뚱한 답을 받게 만들고 있었습니다. 이 글은 그 한 건의 기여를, 무엇이 어긋나 있었고 그것을 어떻게 알아챘으며 한 글자를 고치는 데에도 왜 신경 쓸 거리가 있었는지 처음부터 끝까지 따라가며 정리한 기록입니다. 결과만 보면 글자 하나를 바꾼 변경이지만, 그 글자가 틀렸다고 확신하기까지, 그리고 그것을 고친 흔적을 깔끔히 남기기까지 거친 과정이 저에게는 더 오래 남았습니다.파일 확장자로 MIME 타입을..
지금까지 제가 고친 것들은 대부분 코드의 한 줄이 잘못 동작하는 종류였습니다. 값이 좁혀지거나, 인자가 자리를 바꾸거나, 참조가 끊기는 식이었습니다. 그런데 이번에 다룰 LangChain4j 기여는 조금 결이 다릅니다. 잘못 쓰인 코드가 있어서가 아니라, 있어야 할 한 줄이 아예 빠져 있어서 생긴 문제였습니다. 그리고 그 빠진 자리는 자바 소스가 아니라 빌드 설정 파일, 그중에서도 BOM이라고 부르는 곳이었습니다. 이 글은 그 한 건의 기여를, 무엇이 빠져 있었고 그것을 어떻게 확인했으며 소스가 없는 파일을 어떻게 검증했는지 처음부터 끝까지 따라가며 정리한 기록입니다. 최종 변경은 의존성 항목 하나를 더한 작은 수정이지만, 그 한 항목이 정말 빠진 게 맞는지, 그리고 어떤 버전으로 채워야 하는지를 확신하..
버그 중에는 시끄럽게 터지는 것이 있고 조용히 번지는 것이 있습니다. 예외를 던지며 멈추는 쪽은 적어도 스택 트레이스라는 단서를 남깁니다. 그런데 아무 오류 없이, 화면도 멀쩡하게, 다만 값 하나가 슬그머니 다른 값으로 바뀌어 흘러가는 종류의 버그는 단서를 거의 남기지 않습니다. 이번 글에서 다룰 LangChain4j의 Bedrock 모듈 수정이 바로 그런 경우였습니다. 평범한 크기의 숫자에는 멀쩡하던 변환 코드가, 큰 정수 하나를 만나면 그 값을 조용히 망가뜨려서 내보내고 있었습니다. 이 글은 그 한 건의 기여를, 무엇이 문제였고 그것을 어떻게 확인했으며 어떤 점을 신경 쓰며 고쳤는지 처음부터 끝까지 따라가며 정리한 기록입니다. 최종 변경은 표현식 한 줄을 바꾼 작은 수정이지만, 그 한 줄이 왜 틀렸는..
오픈소스에 기여할 만한 지점을 찾는 일은, 새 기능을 떠올리는 것보다 이미 있는 코드에서 어긋난 부분을 알아채는 쪽이 저에게는 더 수월했습니다. 그중에서도 특히 단서가 분명했던 방법이 하나 있습니다. 이미 머지된 버그 수정 하나를 골라서, 그 수정이 손댄 곳과 똑같은 일을 하는 "형제 경로"가 같은 모듈 안에 또 있는지 살펴보는 것입니다. 같은 종류의 실수는 보통 한 군데에만 있지 않습니다. 누군가 한쪽을 고쳤다면, 같은 패턴을 쓰는 다른 쪽은 아직 안 고쳐졌을 가능성이 꽤 높습니다. 이번 글에서 다룰 LangChain4j Anthropic 모듈의 tool 스키마 수정도 바로 그런 식으로, 이미 머지된 다른 수정을 따라가다 발견한 빈자리에서 출발했습니다. 이 글은 그 한 건의 기여를, 무엇이 문제였고 그..
- Total
- Today
- Yesterday
- TTL 설계
- 백엔드 성능 설계
- DB 트랜잭션
- 백엔드 성능
- 동시성처리
- DB 인덱스 성능
- Initialization-on-Demand Holder Idiom
- 트래픽 처리
- Java Performance
- Redis vs DB
- Cache Aside
- mybatis
- 백엔드 아키텍처
- 트랜잭션 관리
- 캐시 성능 비교
- 캐시와 인덱스
- Double-Checked Locking
- 스레드 생명주기
- InterruptedException
- 캐시 장애
- spring batch 5
- Cache Avalanche
- 백엔드 성능 튜닝
- Cache Penetration
- Enum 기반 싱글톤
- Hot Key 문제
- Redis 성능 개선
- Eager Initialization
- Redis 캐시 전략
- Spring Batch
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
