resource-management

2 개의 포스트

변화하는 세상에서의 스케 (새 탭에서 열림)

클라우드 인프라의 가용 자원이 끊임없이 변동하는 환경에서 중단 없이 실행되어야 하는 비선점형(Non-preemptive) 작업들을 효율적으로 배치하기 위한 새로운 알고리즘이 제시되었습니다. Google Research는 이번 연구를 통해 가용량이 시간에 따라 변하는 환경에서도 작업 처리량(Throughput)을 최대로 확보할 수 있는 최초의 상수 요인(Constant-factor) 근사 알고리즘을 개발했습니다. 이 알고리즘은 변동성이 큰 클라우드 환경에서 작업 손실을 최소화하고 스케줄러의 안정성을 이론적으로 보장하는 기틀을 마련했습니다. ### 동적 클라우드 환경과 스케줄링의 난제 * 현대 클라우드 환경은 하드웨어 장애, 유지보수, 고순위 작업의 자원 점유 등으로 인해 가용 자원이 실시간으로 변동하는 특성을 가집니다. * 특히 비선점형 작업은 한 번 시작하면 중간에 멈출 수 없으며, 자원 부족으로 중단될 경우 지금까지의 모든 작업 진행 내용이 소실되는 리스크가 있습니다. * 스케줄러는 각 작업의 방출 시간(Release time), 마감 기한(Deadline), 처리 시간, 가중치를 고려하여 전체 처리량의 합계(가중치 또는 작업 수)를 극대화해야 합니다. ### 오프라인 설정에서의 최적화 전략 * 미래의 작업 도착 정보와 자원 변동 추이를 미리 알고 있는 오프라인 환경에서는 단순한 그리디(Greedy) 전략이 효과적임이 입증되었습니다. * 가장 먼저 끝나는 작업을 우선 배치하는 그리디 알고리즘은 동일 가치 작업들을 스케줄링할 때 최적해의 최소 1/2 성능을 보장(1/2-approximation)합니다. * 작업마다 가치가 다른 가중치 모델의 경우, Primal-dual 프레임워크를 활용하여 최적해의 1/4 성능을 보장하는 알고리즘을 구현했습니다. ### 온라인 환경의 복잡성과 중단 모델 * 실시간으로 작업이 도착하는 온라인 환경에서는 단 하나의 잘못된 결정(긴 작업 배치)이 미래의 수많은 짧은 작업을 막을 수 있어 기존 방식의 효율성이 급격히 떨어집니다. * **재시작 허용 모델(Interruption with restarts):** 작업 중단 시 진행 데이터는 소실되지만 나중에 다시 시도할 수 있는 모델로, 오프라인과 동일하게 1/2 수준의 경쟁비(Competitive ratio)를 달성할 수 있습니다. * **재시작 불가 모델(Interruption without restarts):** 중단된 작업을 영구히 폐기해야 하는 엄격한 모델로, 일반적인 상황에서는 효율적인 스케줄링이 어렵지만 '공통 마감일'이 있는 실무적 시나리오에서는 해결책을 찾았습니다. ### 공통 마감일 시나리오를 위한 상수 경쟁 알고리즘 * 모든 작업이 동일한 마감 시한을 가지는 실제 배치 작업 환경을 위해 최초의 상수 경쟁 알고리즘(1/11 경쟁비)을 설계했습니다. * 이 알고리즘은 새로운 작업이 도착할 때마다 다음 네 가지 우선순위에 따라 잠정적 스케줄을 갱신합니다. 1. 빈 시간대에 작업 추가. 2. 기존에 예약된 미래 작업보다 현저히 작은 작업으로 교체. 3. 현재 실행 중인 작업의 남은 시간보다 도착한 작업이 더 짧을 경우 실행 중인 작업 중단 및 교체. 4. 위 조건에 해당하지 않을 경우 새 작업 폐기. 이 연구 결과는 자원 공급이 불규칙한 클라우드 시스템에서 이론적 보장을 갖춘 견고한 스케줄러를 구축할 수 있는 근거를 제공하며, 특히 저순위 배치 작업의 효율성을 극대화하는 데 실질적인 도움을 줄 수 있습니다.

코드 품질 개선 기법 20편: 이례적인 예외 과대 포장 (새 탭에서 열림)

리소스를 안전하게 해제하기 위해 사용하는 `use` 패턴이나 커스텀 예외 처리 구현 시, 발생한 여러 예외를 하나의 커스텀 예외로 감싸서(wrapping) 던지는 것은 주의해야 합니다. 이러한 '과대 포장'은 호출자가 기대하는 특정 예외 유형을 가려버려 예외 처리 로직을 무력화시키고 디버깅을 어렵게 만듭니다. 따라서 여러 예외가 동시에 발생할 때는 원인이 되는 주요 예외를 우선시하고, 부수적인 예외는 `addSuppressed`를 통해 전달하는 것이 올바른 품질 개선 방향입니다. ### 예외 과대 포장의 부작용 * 리소스 해제 과정에서 발생하는 예외까지 관리하기 위해 `DisposableException` 같은 별도의 예외 클래스로 감싸게 되면, 원래 발생한 구체적인 예외 정보(예: `IOException`)가 추상화되어 버립니다. * 이 경우 호출부에서 특정 예외를 잡기 위해 작성한 `catch(e: IOException)` 문이 작동하지 않게 되어, 의도치 않은 런타임 오류로 이어질 수 있습니다. * 특히 유틸리티 함수나 보조 함수 내부에서 이러한 포장이 일어날 경우, 호출자는 내부 구현을 상세히 알기 전까지는 예외 처리 실패의 원인을 파악하기 매우 어렵습니다. ### `addSuppressed`를 활용한 예외 우선순위 설정 * 한 코드 블록에서 비즈니스 로직과 리소스 해제(dispose) 로직 모두 예외가 발생할 수 있다면, 어떤 예외가 더 중요한지 판단하여 우선순위를 정해야 합니다. * 일반적으로 비즈니스 로직이 실행되는 `block`에서 발생한 예외가 핵심적인 정보를 담고 있으므로 이를 우선적으로 `throw`해야 합니다. * 리소스 해제 시 발생하는 보조적인 예외는 버리지 않고, 주요 예외의 `addSuppressed` 메서드에 추가함으로써 전체적인 예외 맥락을 보존하면서도 타입 시스템을 해치지 않을 수 있습니다. ### 언어별 예외 처리 시 주의사항 * **Kotlin:** `Closeable.use` 확장 함수는 이미 `addSuppressed`를 활용하여 주요 예외를 우선하는 방식으로 구현되어 있으므로, 커스텀 리소스 클래스 제작 시에도 이와 유사한 패턴을 따르는 것이 좋습니다. * **Java:** Checked Exception이 존재하는 Java에서는 예외를 다른 타입으로 감쌀 때 상속 관계를 신중히 고려해야 합니다. * 복구가 불가능한 경우가 아니라면 Checked Exception을 `RuntimeException`으로 함부로 변환하여 던지지 않아야 하며, 부모 예외 타입으로 뭉뚱그려 잡는 과정에서 예외 처리 누락이 발생하지 않도록 주의가 필요합니다. 리소스 해제와 같은 부수적인 작업에서 발생하는 예외가 본래의 실행 목적을 가진 코드의 예외를 덮어쓰지 않도록 설계해야 합니다. 항상 "어떤 예외가 개발자나 시스템에게 더 중요한 정보인가"를 고민하고, 언어에서 제공하는 예외 억제(suppression) 기능을 활용해 예외의 층위를 명확히 관리할 것을 권장합니다.