반말은 조금 부끄러워서.. 존댓말로 질문드리겠습니다. ItemWriter에서 예외 발생 시 재시도 - 청크 단위로 재시도 관리 1. ItemWriter에서 예외 발생 시 ItemProcessor부터 처리가 재개된다. 2. ItemProcessor에서와 달리, ItemWriter에서의 재시도 횟수는 청크 단위로 관리된다. 요 부분에서 실제로 배치를 돌려보니 writer부터 재시작하는 것을 확인 가능했습니다.. 관련해서 원인을 분석해보니, 아래 결론에 도달했는데 맞을까요? processorNonTransactional() 설정이 켜져있으면 Item 단위로 처리 결과를 캐시에 저장하기때문에 processor는 모두 완료 처리 -> writer부터 시작 processorNonTransactional() 설정이 꺼져있으면 청크 단위로 완료 처리하기때문에 processor부터 다시 시작
킬구형, 청크 지향 처리 시 벌크 read를 할 수 있는 방법이 있어? ItemReader 사용 시 소스에서 1건 씩 데이터를 읽어온다면, 단건 SELECT문이 매번 날아가거나 아니면 JDBC의 ResultSet next()를 사용하는 것 같은데,, 전자면 DB 부하가 너무 심하고 후자여도 네트워크 IO가 꽤 발생할 것 같은데.. 지금 수백? 수천만? 건 정도의 데이터를 마이그레이션 해야하는 업무를 받았는데, 청크 생성 시 DB 부하나 네트워크 트래픽을 좀 줄이고 싶은데 다건 SELECT를 통해 처리하는 방법이 있을까?
오타 발견 root@4ea86c29f1f7:/data# redis-cli set attack:2 "{\"id\":2,\"timestamp\":\"${TODAY}T09:28:47\",\"targetIp\":\"203.0.113.50\",\"attackType\":\"XSS\",\"payload\":\"<script>alert('HACKED!');</script>\"}" 위 명령어 실행 시, 아래와 같은 에러 메시지 발생! bash: !': event not found 에러 발생 원인 bash: !': event not found 에러는 Bash의 히스토리 확장(history expansion) 때문에 납니다. ! 문자(예: HACKED! )가 들어가면 bash가 ! 로 시작하는 히스토리 토큰(예: !! , !$ , !123 )으로 해석하려 하고, 매칭되는 히스토리 항목이 없으면 event not found 오류가 납니다. set +H # 히스토리 확장 끄기 ... redis-cli set attack:2 "{\"id\":2,\"timestamp\":\"${TODAY}T09:28:47\",\"targetIp\":\"203.0.113.50\",\"attackType\":\"XSS\",\"payload\":\"<script>alert('HACKED!');</script>\"}" ... set -H # # 히스토리 확장 켜기 OK redis key들을 set 하기 전에 히스토리 확장을 끄고 실행할 경우 정상 동작
강의 예제를 따라가면서 SystemFailureJobConfig 배치를 실행했는데, 계속 FlatFileItemReader에서 JobParameter로 전달한 inputFile이 null로 들어오는 문제가 발생했다. 정확하게는 Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.item.file.FlatFileItemReader]: Factory method 'systemFailureItemReader' threw exception with message: Path must not be null 이 오류 였으며, public FlatFileItemReader<SystemFailure> systemFailureItemReader( @Value("#{jobParameters['inputFile']}") String inputFile) { log.info("오잉 Reader inputFile: " + inputFile); // null return new FlatFileItemReaderBuilder<SystemFailure>() .name("systemFailureItemReader") //여기서 path not null 오류 발생 .resource(new FileSystemResource(inputFile)) .delimited() .delimiter(",") .names("errorId", "errorDateTime", "severity", "processId", "errorMessage") .targetType(SystemFailure.class) .linesToSkip(1) .build(); } .name("systemFailureItemReader") 이 위치에서 발생하고 있었다. 그리고 line numbers are likely diverged. try to find the current location inside 'SystemFailureJobConfig. systemFaliureItemReader()' 이 메시지도 함께 보였다. GPT에 물어봐서 해결이 되긴 했는데, KillBatchSystemApplication에서 CommandLineRunner를 구현하고 직접 경로를 지정해주었다. @SpringBootApplication @AllArgsConstructor public class KillBatchSystemApplication implements CommandLineRunner { private final JobLauncher jobLauncher; private final Job systemFailureJob; public static void main(String[] args) { SpringApplication.run(KillBatchSystemApplication.class, args); } @Override public void run(String... args) throws Exception { JobParameters params = new JobParametersBuilder() .addString("inputFile", "/Users//Desktop/kill-batch-system2/system-failures.csv") .toJobParameters(); jobLauncher.run(systemFailureJob, params); } } 실무에서는 CLI로 파라미터를 전달하는것이 핵심이라 했던걸로 기억한다. CLI로 전달한 JobParameter를 StepScope Bean에서 안전하게 받는 다른 방법은 없나?? 아 지금 작업도구는 MacOS M1, Spring Boot 3.4.7 이다. 연휴 잘 보내길 바란다.
청크 단위로 트랜잭션 된다는건 이해했다. 그에 관련해서 궁금증이 생겼는데, reader, processor에서 처리하다가 Exception이 나도 사실 DB엔 롤백할게 없으니 사실상 Writer 작업 중에만 단 한번 롤백이 수행될 것이라고 예상되는데 맞는가? read 시에, process 도중에도 롤백이 일어날 경우가 있는지 궁금하다. 추가로 과거 스프링 배치 공식 문서의 잘못된 다이어그램 이라던지 null을 줘야 끝난다는 점과 97개 와 같이 자세한 예시를 들어준 점, read(), process()가 각각 10번씩 수행된다는 것과 같이 사용자들이 많이 헷갈려 하는걸 명확하게 알려줘서 좋은 것 같다.
앞선 질문에 대한 답변 고맙다 미스터 KILL-9 덕분에 손쉽게 엑셀을 처형 (처리)할 수 있었지 하지만 그 다음 관문이 존재하는군 후후.. 각 STEP 마다 RepeatStatus 에 따라서 트랜잭션이 보장하는건 이해했다 하지만 여러 관계가 존재하는 테이블 데이터 적재 시 트랜잭션을 어떻게 보장할지 감이 안온다 미스터 KILL-9 예를 들어 설명하지.. FILE-A, FILE-B 각각 다른 컬럼을 가진 파일들이지 FILE-A는 TABLE-A에 적재하고 FILE-B는 TABLE-B에 적재한다 각 FILE 당 ROW 100개씩 읽으면서 적재하고 문제가 생기면 해당 100건만 롤백할텐데 문제는 TABLE-A 와 TABLE-B의 관계다 TABLE-A 가 부모고 TABLE-B자 자식 테이블인 상황에서 특정 TABLE-B 데이터 롤백 시 TABLE-A 데이터도 마찬가지로 롤백 해야할거 같은데 이런 경우 어떻게 원자성을 보장할 수 있는가에 답변할 수 있겠는가..? 현재 내가 떠오른 방안은 결국은 모든 데이터 처리이니 특정 부분 처리 실패하면 데드 DLQ (Dead Letter Queue) 에 담아서 나중에 처형( 처리)하도록 해야하나 싶다!
Spring Boot + Kotlin + java 21로 진행중이다. 먼저 좋은 강의 너무 고맙고, 나도 다른 사람의 Q&A 처럼 BatchConfig가 있어서 수행시에 부트만 떴다가 꺼지고 아무것도 출력되지 않아서 당황했다 (그래서 강의 자료에 더이상 불필요하다 보다는 제거하라고 명시하면 좋을 것 같다) 질문이 있는데 1. BatchConfig가 있고 없고가 어떤 것 때문에 수행결과에 차이점을 주는거야? 2. Kotlin을 사용한 Spring Boot에서는 @Import를 사용하지 않았는데, 이때도 자동으로 설정을 해주는걸까? BatchConfig가 있다는 것 자체로 배치 수행은 안하고 부트가 바로 끝나버려 두 질문 다 그냥 궁금증일 뿐이다. BatchConfig를 제거하면 수행에는 문제가 없다.
킬구형 안녕 FlatFileReader 라는 아주 편안한 기능은 존재하는데 어째서 ExcelFileReader 기능은 존재하지 않는거지? gpt 말로는 공식 확장 모듈은 spring-batch-excel 이 존재한다고는 하는데 이거 믿고 사용해도 되는지 모르겠네 킬구형은 excel 파일 읽고 DB에 쓸 때 아파치 poi 라이브러리로 직접 파싱해?
InFearLearn 서버 침입 사건: 해커 패턴 추적 작전 작전 수행을 위한 실전 코드부터 살펴보자. 여기코드에서 @Bean @StepScope public MongoCursorItemReader<SecurityLog> securityLogReader( 이 아이템리더쪽 복붙하고 안다듬어서 들여쓰기 밀림
공부중인데 내용이 너무 좋아서 블로그에 공부한 예제 코드와 정리된 내용을 일부 작성하려 하는데 괜찮을까요?? 스크린샷으로 첨부하거나 그런건 아니고 코드는 제 ide 에서 실행한것과 git repo 에 올리면서 공유하려고 해~ 그리고 공부한 내용들은 직접 작성 하고 정리해서 올리려고 하는데 괜찮은지 궁금해서 질문해!!
1.오타 JobScope와 StepScop가 선언된 빈은 애플리케이션 구동 시점에는 우선 프록시 객체로만 존재한다. 그 후 Job이나 Step이 실행된 후에 프록시 객체에 접근을 시도하면 그 때 실제 빈이 생성된다. StepScop <-오타 2.건의(내가틀렸을수도있음 그러면 수정해주셈) 그리고 JobParameters가 잡 실행 내부에서 불변이라는 내용이 필요할거같음 배치쓰던사람들은 당연한거라 생각할수있는데(잡파라미터랑 잡이름으로 유니크체크한댔나 그런거도 있으니까) 배치 첨쓰는사람은 왜 잡파라미터가 있는데 ExecutionContext를 사용하지 라는 생각을 할수있을거같음 3.궁금증 컴파일 시점에 없는 값을 어떻게 참조할 것인가? 여기서 잡의 스텝생성시점에서 di받는 잡파라미터 자리에 null을 넣는식으로 처리하는 방법이 있다고했는데,만약 코틀린의 경우엔 명시적으로 잡파라미터를 ?를 붙여서 nullable로 선언하고 로직에서 NullSafe 박는식으로밖에 해결할수없고 저게 맘에안들면 빈주입해야함? 저런 null전달같은 꼼수딴거없음? 강의가 자바기반이라고 적혀있어서 물어봐도되나싶긴한데 예전에 저것땜에 고생했었는데 다른방법을 못찾아서 물어봄
킬구형 안녕! 지금 실무에서 Spring Batch를 사용해서 배치 기능 구현하고 있는데 궁금한 점이 있어. 강의의 예제 코드들을 보다보면 Job에 필요한 항목들을 따로 클래스 파일로 빼서 구현하지 않고 JobConfig 클래스 내에서 이너 클래스로 구현하던데 특별한 이유가 있을까!? @Slf4j public static class BrainwashProcessor implements ItemProcessor<InFearLearnStudents, BrainwashedVictim> { 강의가 너무 재밌어서 점심시간에도 공부 중이야 ㅎㅎ
킬구형 안녕, 강의를 보다가 궁금한 점이 생겨서 문의를 남겨. "정리하자면, allowStartIfComplete 은 식별 파라미터 없이 Job을 재시작한 경우에만 적용되는 옵션이다." 이 문장을 보고 궁금증이 생겼는데, identifying JobParameters 가 없어서 Job 그리고 그 하위 Step 들이 다시 수행될수도 있겠지만, Job => Step A (성공) / Step B (실패) 여서 잡을 재시작하는 케이스에서, Step A 가 allowStartIfComplete true 면 A부터 스텝을 수행할텐데, 이 케이스에서도 allowStartIfComplete 를 쓰는게 아닌가 싶어서 문의를 남겨! 이러면 Spring Batch 6 에서도 의미있는 파라미터가 아닌가 싶어서! (강의 잘보고 있습니다, 감사합니다!!)
,로분리된 csv파일로 하고 public FlatFileItemReader<SystemFailure> systemFailureItemReader( @Value("#{jobParameters['inputFile']}") String inputFile) { return new FlatFileItemReaderBuilder<SystemFailure>() .name("systemFailureItemReader") .resource(new FileSystemResource(inputFile)) .delimited() .delimiter(",") .names("errorId", "errorDateTime", "severity", "processId", "errorMessage") .targetType(SystemFailure.class) .linesToSkip(1) .strict(true) .build(); } ---------- 실행하면, 오류가 발생합니다. PS D:\Test\springboot\kill-batch-system> ./gradlew bootRun --args='-- spring.batch.job.name =systemFailureJob inputFile=d:/Test/springboot/kill-batch-system/system-failures.csv' ..... Caused by: org.springframework.batch.item.file.transform.IncorrectTokenCountException: Incorrect number of tokens found in record: expected 5 actual 1 ..... PS D:\Test\springboot\kill-batch-system> 그런데, \t으로 구분하고, .delimiter("\t") 로 하면 정상작동합니다. 이유가 뭘까요? 윈도우 환경입니다. 그리고, 실행시 파일의 위치를 절대경로로 주어야 하나요? 상대경로일경우, 파일의 위치를 어디에 두어야 하나요?