묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 배치
StepExecutionListener 의 afterStep 에서 return ExitStatus.FAILED 에 의한 동작에 의문이 갑니다.
안녕하세요 선생님, 개인적으로 이 강의를 다 보고,혼자서 이것저것 해보고 있는데, 뭔가 이해가 안되는 현상이 있어서 질문드립니다. 일단 제가 작성한 아주 간단한 Batch 코드를 먼저 공유하겠습니다. (Spring boot 3 + Spring Batch core 5 버전입니다.)```javapackage coding.toast.batch.job.reader.config; import coding.toast.batch.job.reader.listener.EmptyReadFailureListener; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.item.support.ListItemReader; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; import java.util.List; @Configuration @RequiredArgsConstructor public class CustomItemStreamConfig { private final JobRepository jobRepository; private final PlatformTransactionManager transactionManager; @Bean public Job customItemStreamConfigJob() { return new JobBuilder("customItemStreamConfigJob", jobRepository) .start(customItemStreamConfigStep()) .next(simpleStep2()) .build(); } private Step simpleStep2() { return new StepBuilder("simpleStep", jobRepository) .tasklet((contribution, chunkContext) -> { System.out.println("이게 나오면 안됩니다!"); return RepeatStatus.FINISHED; }, transactionManager).build(); } @Bean public Step customItemStreamConfigStep() { return new StepBuilder("customItemStreamConfigStep", jobRepository) .<Integer, Integer>chunk(5, transactionManager) .reader(new ListItemReader<>(List.of())) // 고의적으로 비워놨습니다! .writer(chunk -> { chunk.getItems().forEach(System.out::println); }) .listener(emptyReadFailureListener()) .build(); } @Bean public EmptyReadFailureListener emptyReadFailureListener() { return new EmptyReadFailureListener(); } } 여기서 EmptyReadFailureListener 타입의 인스턴스를 빈으로 등록하고, 이를 Step 의 listener 로 등록했습니다. 이 리스너의 역할은 ItemReader 로 부터 읽은 것이 아무것도 없을 때 Step 이 FAILED 되도록 하기 위한 것입니다. 자세한 내용은 아래와 같습니다.package coding.toast.batch.job.reader.listener; import org.springframework.batch.core.*; public class EmptyReadFailureListener implements StepExecutionListener { @Override public ExitStatus afterStep(StepExecution stepExecution) { if (stepExecution.getReadCount() > 0) { return stepExecution.getExitStatus(); } else { return ExitStatus.FAILED.addExitDescription("exit Because Of No Item Read"); } } } 위처럼 작성했습니다.저는 위의 Job 을 실행하면...Job 의 BatchStatus 와 ExitStatus 모두 FAILED 가 된다.simpleStep 은 이전 Step 에서 ExitStatus Failed 가 됐으므로 실행되지 않는다. ... 하지만 정확히 정반대의 결과가 나왔습니다 😅😅 batch_job_execution 테이블의 결과: batch_step_execution 테이블의 결과: 보시는 바와 같이 Job 자체는 Completed 로 끝이 났습니다.또한 실행되지 않았으면 했던 두번째 step(=simpleStep) 도 실행이 되버리더라구요... 질문:1. 왜 이렇게 동작하는 걸까요? 혹시 ExitStatus 에 대해서 뭔가 잘못 알고 있는 걸까요?2. 그리고 이를 해결하기 위해서는 EmptyReadFailureListener 를 어떻게 고치면 좋을까요? 아니면 혹시 더 좋은 방법이 있을까요??
-
미해결스프링 배치
jdbc, jpa 커서방식 조회 방식 차이 질문 (강사님께 답변 받고 싶습니다)
안녕하세요커서방식에서 Jdbc는 최초 open 메서드 호출 때 db에 쿼리를 날려 ResultSet 첫번 째 row를 가져온 상태에서 read() 를 반복해서 db resultset 커서를 하나씩 이동 시켜 스트리밍 방식으로 가져오는 걸로 알고 있습니다.jpa도 마찬가지로 최초 open 메서드에 쿼리를 날려 작업 결과를 가져오는데 이 때 ResultStream을 반환하고 read에서 실제 db와 상호작용하지 않고 list의 반복자 패턴으로 하나씩 가져온다고 이해했는데요. read때 db와 상호작용을 하지 않는다고 하면 처음 ResultStream에 결과를 다 가져온다는 소리인데 이렇게 되면 메모리 효율이 떨어지지 않나요?
-
미해결스프링 배치
Multithread step과 AsyncItemProcessor
read 작업이 무겁고(느림) processor 작업에는 I/O(api call 등,,)가 많다고 가정했을때 Multithread step(reader는 스레드안전)과 AsyncItemProcessor를 함께 사용해도 문제가 없을까요??Multithread step 자체도 별도의 스레드 풀에서 동작하고AsyncItemProcessor도 다른 스레드 풀에서 작동하고reader(reader는 스레드안전)에서 데이터를 가져올때 이미 작업범위가 정해져있기 때문에 동시성으로 부터도 안전해 보여서요!
-
미해결스프링 배치
job 재실행
안녕하세요 강사님 좋은 강의 감사드립니다.강의의 코드를 따라하며 실습을 하고 있는데 job이 실패했을 경우에만 재시작이 가능하고 completed로 정상적으로 종료되었을 경우에는 재실행이 안된다는 것을 숙지하고 있습니다.(파라미터 없거나 동일할 경우)근데 현재 flow 관련 실습을 하다보니 job 실행 시 아무런 파라미터도 주지 않고 Job이 completed로 정상적으로 끝났음에도 불구하고 Job이 재실행되는 경우가 있는데 이는 어떤 이유 때문인지 알려주실 수 있을까요?step 내부에서 contribution을 통해 extiStatus를 FAILED로 설정한 것 때문인지, 이외에도 다른 이유가 있는지 궁금합니다.확인해주시고 답변 주시면 많은 도움이 될 것 같습니다.감사합니다.
-
미해결스프링 배치
bean 생명주기 문제 도와주세요(@Scope("step"), @Autowired)
안녕하세요. 문제가 있는데 해결 방법이 떠오르지 않아 문의드립니다.현재 스프링 배치 4.1.4 버전을 사용하고 있고, 스프링 부트는 2.1.4 버전을 회사에서 사용하고 있습니다. 스프링 배치에서 JobListener를 이용해 에러가 발생할 시 잡의 마지막 부분에서 SMS 전송 시스템을 세팅해서 SMS 전송하도록 사용하려고 하는데요.문제는 아래와 같습니다.@Scope("step") 어노테이션을 사용해야 Spel 의 값을 가져올 수 있음@Component 어노테이션을 달고, JobListener에서 @AutoWired 어노테이션을 사용하려고 할 때 @Scope("step") 영향 때문인지 해당 bean을 찾지 못함 즉 문제를 정리하자면, SMS class에서 Spel 의 값을 가져와야 값을 세팅할 수 있으므로, @Scope("step") 어노테이션을 달아야하는데 이 어노테이션을 붙이면 bean의 생명주기 영향으로 @AutoWired 어노테이션이 작동하지 않는 것 같습니다. 이럴 땐 어떻게 해결해야 하나요?그리고 @Scope("step")을 아직 학습하진 않았으나 찾아보니 스프링 배치에서 사용하는 특별한 scope로 step이 실행되고 있을 때에만 해당 bean이 컨텍스트에서 유지되는 것으로 학습했습니다. 이것이 맞나요? --추가JobExecutionLisener - beforeJob 메서드 부분에 jobExecution.getExecutionContext(); 로 ExecutionContext 인스턴스를 가져온 뒤, 해당 인스턴스에 Map 형태로 값을 집어 넣습니다.예를 들어, jobContext.put("jobId", jobId) 이런 형식으로 값을 세팅해놓고, 각 스텝에서 이 값들을 사용할 땐 @Value("#{jobExecutionContext['jobId']}" 이런 형태로 Spel를 사용합니다. 추가적으로 궁금한 부분은 Value 어노테이션에 어떻게 jobExecutionContext 값이 먹히는지 모르겠습니다.원래는 이름이 똑같아야 하지 않나요? ExecutionContext 인스턴스니까 @Value("#{ExecutionContext['jobId']} 이렇게 사용을 해야할 거 같은데 왜 앞에 job이 붙어야 하는지 잘 모르겠습니다.
-
미해결스프링 배치
Multi-threaded-step과 Partitioning 차이 확인
안녕하세요 Multi-threaded-step과 Partitioning 차이가 제가 알고 있는 게 맞는지 궁금하여 질문드립니다.Multi-threaded-step은 하나의 Step 안에서 Chuk 크기만큼읽기-처리-쓰기 이 한 사이클을 여러 스레드들이 독립적으로 수행되어 단일 스레드보다 속도 향상을 할 수 있고PartitionStep은 하나의 Master Step 안에 파티셔닝 할 Slave Step을 정의하고 이 Step은 공유되고 gridSize 만큼 스레드가 생성되어 해당 스레드에는 독립적으로 스택에 StepExecution 참조를 할당받아 마치 여러 Step들이 스레드 개수만큼 수행되는 건가요? 즉 Multi-threaded-step은 Step in Muti-Thread 이고Partitioning은 Step in Multi-Step 인거죠?
-
미해결스프링 배치
jdbcCursorReader, jdbcPagingReader 질문
안녕하세요 jdbc 를 통해 데이터를 읽어오는 점에 대해 질문이 있습니다.@Bean public ItemReader<Customer> jdbcCursorItemReader() { return new JdbcCursorItemReaderBuilder<Customer>() .name("jdbcCursorItemReader") .fetchSize(10) .dataSource(dataSource) .sql( "select id, firstName, lastName, birthDate from customer where firstName like ? order by lastName, firstName") .beanRowMapper(Customer.class) .queryArguments("A%") .build(); }여기서 fetchSIze 는 db 에서 한번에 가져올 레코드 수이고 @Bean public ItemReader<Customer> jdbcPagingReader() throws Exception { Map<String ,Object> paramMap = new HashMap<>(); paramMap.put("firstName", "H%"); return new JdbcPagingItemReaderBuilder<Customer>() .name("jdbcPagingReader") .dataSource(dataSource) .fetchSize(3) .pageSize(3) .queryProvider(pagingQueryProvider()) .parameterValues(paramMap) .beanRowMapper(Customer.class) .build(); }여기 페이징 방식에는 fetchSize, pageSize 두개를 둘 다 사용할 수 있던데 차이가 정확인 뭔가요? pageSize는 설정 시 쿼리에 Limit 설정 수가 추가되어 나가는 것은 확인했습니다.
-
미해결스프링 배치
step muti-thread 질문
안녕하세요 step 에서의 멀티 스레드 동작 방식에 대해 질문이 있습니다. 4개의 스레드 풀을 관리하는 taskExecutor 를 사용한다고 하였을 때 제일 맨 처음 스레드가 db 에서 fetch size 만큼 가져오고 시작하게 되는데 각 스레드들은 독립적으로 읽기-처리-쓰기 한 싸이클을 독립적으로 처리하게 되는게 맞나요.?아니면 또 스레드1이 읽은 데이터를 스레드 2가 처리하는 방식인가요.?요약은 스레드1이 읽은 데이터를 쓰기 작업까지 한 싸이클을 끝까지 독립적으로 수행하는가 아닌가 입니다.
-
미해결스프링 배치
itemSteam open update close 질문
안녕하세요 강의 중 궁금한 사항이 있어 질문드립니다.chunkSize 가 만약 5이고 데이터 사이즈가 10이라면step을 실행할 때 최초 reader, writer 의 open 메서드를 실행시키고 최초 update 실행 후 chunk process (read, process, write) 5번 주기로 update를 실행, 마지막으로 reader, writer close 해주는걸로 알고 있는데요 여기서 update는 총 3번 진행되는게 맞는건가요? -> 최초 1회, 5번일 때 1회, 10번일때 1회
-
미해결스프링 배치
@JopScope, stepScope 지연 초기화 관련 질문
안녕하세요 강사님스코프 객체 지연 초기화에 대해 질문이 있습니다.해당 어노테이션을 사용하면 스프링 컨텍스트 초기화 시점에실제 객체 지연 초기화를 위해 컨테이너에 프록시 객체를 미리 등록해 놓고 해당 객체의 메서드 사용 시점에 실제 객체가 생성되는 것으로 알고 있는데요 이 때 실제 객체를 생성하면은 스프링 컨테이너에 빈으로 또 다시 등록하나요? 아니면 실제 객체 생성 후에 참조만 프록시에게 넘겨주어 동작하게 되는건가요?
-
미해결스프링 배치
Transition - on() / to() / stop(), fail(), end(), stopAndRestart() 강의의 38:39에 대한 내용 질문
FlowJob에 대한 강의를 듣다가궁금한 점이 있어서 질문드립니다. from()에 관한 질문인데요,from()의 역할이 현재 트랜지션에서 벗어나새로운 트랜지션을 다시 재정의하는 것으로 이해했습니다. 그래서 38:39 부분에서,step1()은 처음에 start()에서 정의하고,30번 째 라인에서, 다시 step1()의 기준을 재정의하FAILED가 아닌 모든 경우에 대한 flow를 재구성하는 것으이해했습니다. 질문은 step2()에 관한 것입니다.step2()는 첫번째 트랜지션인]on("FAILED") 하위에 to()를 통해서 최초에 정의되었습니다. 하지만, 그리고 step2()는 "FAILED" 상태인 경우에,FlowJob을 Stop하고 그 결과로 Job을 상태를 Stopped로 만듭니다. step2의 중점 Flow는step1() -> FAILED -> step2() -> FAILED -> STOPED인 것을 알 수 있습니다. 그런데, 34번 라인에서 다step2를 가져와서 이전에 정의 FAILED를 제외한 나머지 경우를 진입시키는 Transtion을 작성하였습니다. 코드만 보면, 계층이 step1과 나란한 구조라서,헷갈릴 것 같습니다. 그래서, 저는 34~36번 라인의 코드를,29번 라인 이후에,작성해도 같은 결과를 보장하는지 궁금합니다. from절이 어디에 사용돼도,항상 from절 내부에 있는 StepBean을 기준으다시 flow가 재정의 된다고 봐도 되는걸까요?
-
미해결스프링 배치
job launcher관련 질문 있습니다.
joblauncher 동기적 방식 설명을 보면 controller 를 하나 만들job di 로 받고 있는데요 job 이 여러개 있는 경우 예를 들어 a, b, c 3개의 잡이 있을 때 controller 에 a job 을 전달하려면 어떻게 해야되나요?
-
미해결스프링 배치
slave 청크의 실패 횟수에 따라 나머지 청크를 중지시키는 방법이 있을까요?
return new StepBuilder("apiStep", jobRepository) .<HelloVO, Hello>chunk(CHUNK_SIZE, transactionManager) .reader(helloReader(null)) .processor(helloVO -> { LocalDate date = helloVO.getDate(); if(date.getDayOfMonth() == 10){ throw new IllegalStateException("!! " + helloVO); } return helloVO.toEntity(); }) .writer(v -> log.info("write = {}", v)) .listener(partitionLimitSkipListener(null)) .build(); 안녕하세요.만약 위 상황에서 파티션이 1000개라고 가정할 경우 천 건의 청크가 동작하게 될텐데요. 만약 첫 번재 값이 timeout으로 예외가 발생할 경우, 남은 999건 동일한 timeout이 발생할 수 있다고 예상되고 이런 에러는 장애로 확장될 수 있다고 예상됩니다. 실제로 위의 throw ex 상황에서 failed로 처리된 1000건의 step이 발생함을 확인하였습니다. 이런 문제를 최소화 해야 하는 것에 목적이 있습니다.이를 해결하기 faultTolerant로 사용하고 chunkListener의 afterError메서드로 step context까지 도달하여 setTerminal 을 하였으나... 이 방법으로는 해결이 되지 않더라고요. 결국 결국 아래와 같이 operator로 처리하였습니다. @Slf4j public class ChunkExceptionCounterListener implements ChunkListener { private int failureCount = 0; private static final int MAX_FAILURES = 3; private final JobOperator jobOperator; public ChunkExceptionCounterListener(JobOperator jobOperator) { this.jobOperator = jobOperator; } @Override public void afterChunkError(ChunkContext context) { failureCount++; log.error("chunk {} exceeds error {}/{}", context.getStepContext().getStepName(), failureCount, MAX_FAILURES); if (failureCount >= MAX_FAILURES) { log.error("stop job! with afterChunkError!"); context.getStepContext().getStepExecution().setTerminateOnly(); // 안됨..ㅠ context.getStepContext().getStepExecution().getJobExecution().setExitStatus(ExitStatus.FAILED); Long jobExecutionId = context.getStepContext().getStepExecution().getJobExecutionId(); try { jobOperator.stop(jobExecutionId); // 이건 된다! } catch (NoSuchJobExecutionException e) { throw new RuntimeException(e); } catch (JobExecutionNotRunningException e) { throw new RuntimeException(e); } } } } 위 방식이 좋은 방식인지는 솔직히 모르겠습니다. 일단 기대하는 것처럼 interrupt로 에러로 남은 모든 step이 stopped되어 좋긴 하였는데, 제가 혼자서 구현한 방식인지라... 최적의 방식인지는 잘 모르겠네요. 더불어 retry template에서 recover callback 메서드를 구현할 경우 청크 파티션 일부의 에러를 마스터 슬레이브 전체에 대한 정지 처리로 할 수 있던 것으로 기억하는데, 이 정도의 튜닝은 제공하는지도 궁금합니다. 감사합니다^^
-
미해결스프링 배치
바쁘신데 죄송하지만 답변좀 부탁드립니다 교수님!
https://www.inflearn.com/course/lecture?courseSlug=%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B0%B0%EC%B9%98&unitId=91677&tab=community&q=1359231&category=questionDetail
-
미해결스프링 배치
어플리케이션 예제(2) 엑셀파일이 안읽어와집니다ㅠ
package springbatch.batch.job.file; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.database.JpaItemWriter; import org.springframework.batch.item.database.builder.JpaItemWriterBuilder; import org.springframework.batch.item.file.FlatFileItemReader; import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder; import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import springbatch.batch.chunk.processor.FileItemProcessor; import springbatch.batch.domain.Product; import springbatch.batch.domain.ProductVO; import javax.persistence.EntityManagerFactory; @Configuration @RequiredArgsConstructor public class FileJobConfiguration { private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; private final EntityManagerFactory entityManagerFactory; @Bean public Job fileJob() { return jobBuilderFactory.get("fileJob") .start(fileStep1()) .build(); } @Bean public Step fileStep1() { return stepBuilderFactory.get("fileStep1") .<ProductVO, Product>chunk(10) .reader(fileItemReader(null)) .processor(fileItemProcessor()) .writer(fileItemWriter()) .build(); } @Bean @StepScope public FlatFileItemReader<ProductVO> fileItemReader(@Value("#{jobParameters['requestDate']}") String requestDate) { return new FlatFileItemReaderBuilder<ProductVO>() .name("flatFile") .resource(new ClassPathResource("product_" + requestDate +".csv")) .fieldSetMapper(new BeanWrapperFieldSetMapper<>()) .targetType(ProductVO.class) .linesToSkip(1) .delimited().delimiter(",") .names("id","name","price","type") .build(); } @Bean public ItemProcessor<ProductVO, Product> fileItemProcessor() { return new FileItemProcessor(); } @Bean public JpaItemWriter<Product> fileItemWriter() { return new JpaItemWriterBuilder<Product>() .entityManagerFactory(entityManagerFactory) .usePersist(true) .build(); } }application.ymlspring: profiles: active: mysql jpa: hibernate: ddl-auto: update database-platform: org.hibernate.dialect.MySQL5InnoDBDialect show-sql: true open-in-view: false properties: hibernate.format_sql: true --- spring: config: activate: on-profile: mysql datasource: hikari: jdbc-url: jdbc:mysql://localhost:3306/springbatch?useUnicode=true&character_set_server=utf8mb4 username: root password: ---- driver-class-name: com.mysql.cj.jdbc.Driver batch: job: names: ${job.name:NONE} enabled: false jdbc: initialize-schema: always pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>spring-batch</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-batch</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-batch --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> <version>2.5.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>2.4.4</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>9.0.0</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>5.3.7</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.20</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <dependency> <groupId>org.springframework.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> 잘 작성한거 같은데, 디버그 중단점 걸어놓아도 안걸러지네요ㅠㅠ 원인을 모르겠습니다..requestDate 부분이랑 fileItemReader이 동작을 안하는거 같습니다.
-
미해결스프링 배치
JdbcCursorItemReader 질문 드립니다.
안녕하세요. 좋은 강의 잘 듣고 있습니다.조금 헷갈리는 부분이 있는데JdbcCursorItemReader 를 사용하면서 chunk size를 10으로 설정해서 100개의 데이터를 처리한다면메모리에는 데이터가 최대 10개가 올라가는건가요?아니면 100개까지 올라가는건가요?몇백만건의 데이터를 처리할 때 사용가능한건지 궁금합니다. 그리고 커넥션을 한번 맺으면 끝까지 물고 있다고 이해했는데몇백만건 처리를 할때 3시간이 걸린다면 실무에서 타임아웃도 3시간 이상으로 설정을 하나요?
-
미해결스프링 배치
StepBuilderFactory, JobBuilderFactory Inspection 'Incorrect autowiring in Spring bean components' options
안녕하세요 ! 강의 듣고 있는데 실습해보고 싶어서 계속 따라하는데 빈 등록이 자꾸 안된다고 뜹니다 ㅠ 혹시 호환성에 대해 문제가 있을까바 검색해봤는데도 호환성에 대해서는 잘 작동한다고도 하는데 왜 자꾸 빈으로 등록이 안되는걸까요 ?spring boot 3.3.2 사용하고 jdk는 17 사용합니다.제가 지금까지 해본것은 1. @RequiredArgsConstructor 을 이용한 의존성 주입 2. 생성자를 생성하여 @Autowired를 사용 3. batch 호환성에 대한 문제 아니면 혹시 Maven으로만 작성해서 그런걸까요 ? 답변 부탁드립니다 ㅠ
-
미해결스프링 배치
chunk 처리 기반에서 cursor 방식과 메모리 사용
안녕하세요. 비전공자로 배치 프로그램에 관심이 많아서 해당 강의를 듣다가 커서 방식과 페이징 방식을 스프링 배치에서 사용할 때 고려사항과 관련하여 질문이 있습니다. 커서가 페이징 방법에 비해 데이터 일관성이 보장이 쉽고 속도도 더 빠른 것으로 알고 있습니다. 그리고 청크 기반 배치 처리 로직으로 구현한다면, 커서 기법을 사용하는 경우에도 페이징 방법처럼 트랜잭션 단위를 청크 크기만큼 나눌 수 있을 텐데요.(페이징에서 chunk와 paging 단위를 같이 하는 것처럼 커서에서도 chunk와 fetchsize를 같게 해주면 될 것으로 고려합니다.)그리고 멀티 스레드를 고려한다면,SynchronizedItemStreamReader로 reader 영역을 감싸서 처리할 수도 있는 걸로 알고 있습니다. 그렇다면, 이러한 상황 속에서 커서와 페이징을 사용함에 고려해야할 트레이프 오프가 커넥션 타임 뿐인 걸까요??이런 상황에서도 커서가 메모리 할당 방식으로 인해서 메모리 사용량이 커진다는 단점이 있는 건가요??( 더 정확하게 말하자면 결과를 메모리에 할당한다는 점이 명확히 와닿지 않습니다.제 나름 추론하자면,커서가 fetch size나 chunk로 전체 데이터를 분할하여 메모리에 올리고 읽고나서도 모든 결과값이 메모리에 쌓여 한번에 write(ex- db에 저장) 되기 때문에 메모리 사용량이 많아 진다는 것인가여??반면에, 페이징은 기반 처리는 페이징 단위로 데이터를 읽고 읽은 데이터 크기를 처리 후 메모리에 올리고 write한 후에 다시 정해진 크기 만큼 읽는 방식인건가요??즉, 이것이 커넥션을 맺고 끊는 것 방식과 관련이 깊은 것인지 궁금합니다.)이상 cs적인 지식이 부족한 비전공자의 긴 질문을 읽어주셔서 감사합니다.
-
미해결스프링 배치
스프링 배치 5 변경점 정리
5버전으로 강의 따라가면서 정리하고 있습니다(현재진행중). 공유하고자 올려봅니다.혹여나 틀린점 있으면 지적해주세용.Execution context serialization Updateshttps://github.com/spring-projects/spring-batch/wiki/Spring-Batch-5.0-Migration-GuideExecutionContext에 Base64로 인코딩된 데이터가 저장됩니다.@Configuration public class BatchConfig { @Bean public ExecutionContextSerializer jacksonSerializer() { return new Jackson2ExecutionContextStringSerializer(); } }강의처럼 데이터를 json으로 저장하고 싶으면, jackson-core의존성을 추가한 후, 위 설정클래스를 정의합니다.섹션2~3@EnableBatchProcessing 을 쓰게되면 BatchAutoConfiguration이 적용되지 않는다.https://umbum.dev/1320/자동 구성과 수동 구성 요소가 동시에 존재할 때 발생할 수 있는 모호성과 충돌을 피하기 위해BuilderFactory대신 JobBuilder, StepBuilder 직접 사용팩토리 클래스들은 종종 불필요한 복잡성을 추가하고, Spring Batch 구성의 일관성을 떨어뜨림.직접 사용해서 더 많은 제어 권한 획득Tasklet 사용시 , PlatformTransactionManager 함께사용 (단일 사용 deprecated)배치 설정 클래스 BasicBatchConfigurer → DefaultBatchConfiguration으로 변경 섹션 3 - JobLauncher주입받은 JobLauncher가 프록시객체가 아닌 실제 객체라서 DefaultBatchConfiguration 에서 JobLauncher를 얻어올 필요없이 바로 타입캐스팅 가능섹션 4 - 배치 초기화 설정spring:batch:job: names(x) -> name실행 할 job을 찾지 못하면 예외가 발생합니다. 인텔리제이에 환경설정으로 프로그램 인수를 한개만 할당할 수 있게 변경되었습니다.파라미터 관련해서는 코드로 할당해줘야 합니다.multiple jobs 실행 불가능.JobLauncherApplicationRunner 의 executeLocalJobs 메서드에서 split으로 job 이름을 구분하는 코드가 삭제되었습니다.섹션 7 - 스프링 배치 청크 프로세스 이해chunk사용시 , PlatformTransactionManager 함께사용 (단일 사용 deprecated)ItemWriter의 write(List<? extends String> items ) -> write(Chunk<? extends String> chunk)Items에 접근하려면 getItems로 꺼내야합니다.
-
미해결스프링 배치
ExecutionContext 질문
안녕하세요. 아래 코드에서 둘 다 ExecutionContext가 반환되는데, 둘은 같은 ExecutionContext가 아니라 다른 ExecutionContext인가요?메서드 참조로 계속 타고 들어가서 하나는 getJobExecution()에서, 나머지 하나는 getStepExecution()에서 꺼내오는데 둘다 동일한 타입의 ExecutionContext라 헷갈립니다.그리고 저렇게 각각 꺼내올 때 다른 ExecutionContext라 JobExecutionContext에 저장된 값을 Step에서 공유가 가능하고, stepExecutionContext에 저장된 값은 Step에서 공유가 불가능한 것이 맞는지 궁금합니다.ExecutionContext jobExecutionContext = stepContribution.getStepExecution().getJobExecution().getExecutionContext(); ExecutionContext stepExecutionContext = stepContribution.getStepExecution().getExecutionContext();