Skip to content

Latest commit

ย 

History

150 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๊ธฐ์ˆ  ๋ฌธ์„œ: ๋Œ€์šฉ๋Ÿ‰ ์ •์‚ฐ/์ฒญ๊ตฌ ๋ฐฐ์น˜ ์‹œ์Šคํ…œ v1.1

1. ์‹œ์Šคํ…œ ๊ฐœ์š”

๋ณธ ๋ฌธ์„œ๋Š” Spring Batch ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์ถ•๋œ '์›”๋ณ„ ์ฒญ๊ตฌ๋‚ด์—ญ ๋ฐ ์ฒญ๊ตฌ์„œ ์ƒ์„ฑ ์‹œ์Šคํ…œ'์˜ ๊ตฌํ˜„ ์‚ฌํ•ญ์„ ๊ธฐ์ˆ ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ์‹œ์Šคํ…œ์€ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ํ™˜๊ฒฝ์—์„œ๋„ **์•ˆ์ •์ ์ธ ์ฒ˜๋ฆฌ, ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ, ์žฌ์‹คํ–‰ ๊ฐ€๋Šฅ์„ฑ(Restartability)**์„ ํ•ต์‹ฌ ๋ชฉํ‘œ๋กœ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์‹œ์Šคํ…œ์€ ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ์˜ ์•ˆ์ •์ ์ธ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Partitioning(๋ฐ์ดํ„ฐ ๋ถ„ํ• ) ๊ธฐ๋ฒ•์„ ์ ์šฉํ•˜์˜€์œผ๋ฉฐ, ์žฅ์•  ๋ฐœ์ƒ ์‹œ ์‹คํŒจ ์ง€์ ๋ถ€ํ„ฐ์˜ ์žฌ์‹œ์ž‘(Restartability)์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

1.1 ํ•ต์‹ฌ ๊ตฌํ˜„ ๋ชฉํ‘œ ๋‹ฌ์„ฑ ํ˜„ํ™ฉ

๊ตฌ๋ถ„ ์„ค๊ณ„ ๋ชฉํ‘œ ๊ตฌํ˜„ ๋‚ด์šฉ (v1.0)
ํ”„๋ ˆ์ž„์›Œํฌ Spring Batch com.template.worker.global ๊ธฐ๋ฐ˜ ๊ณตํ†ต ๋ชจ๋“ˆ ๊ตฌํ˜„ ์™„๋ฃŒ
์ฒ˜๋ฆฌ ๋ฐฉ์‹ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ (Partitioning) PartitionHandler, TaskExecutor ๊ธฐ๋ฐ˜ Masterโ€“Worker ๊ตฌ์กฐ ๊ตฌํ˜„
๋Œ€์ƒ ์ž‘์—… ์ฒญ๊ตฌ๋‚ด์—ญ / ์ฒญ๊ตฌ์„œ ์ƒ์„ฑ InvoiceItemJob(์ฒญ๊ตฌ๋‚ด์—ญ), InvoiceJob(์ฒญ๊ตฌ์„œ) 2์ข… Job ๊ตฌํ˜„
I/O ๋ฐฉ์‹ JDBC Paging Reader JdbcPagingItemReader ๊ธฐ๋ฐ˜ ์กฐํšŒ ์ตœ์ ํ™” ์ ์šฉ
Write ๋ฐฉ์‹ Batch Insert JdbcBatchItemWriter ๊ธฐ๋ฐ˜ ์ผ๊ด„ ์‚ฝ์ž… ์ ์šฉ
์•ˆ์ •์„ฑ ์žฌ์‹œ์ž‘ / ๋ฉฑ๋“ฑ์„ฑ ExecutionContext + UNIQUE ์ œ์•ฝ ์ ์šฉ

2. ๋ฐฐ์น˜ ์‹คํ–‰ ์•„ํ‚คํ…์ฒ˜ (Partitioning Model)

๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด Single Thread ๋ฐฉ์‹์ด ์•„๋‹Œ, ๋ฐ์ดํ„ฐ ๋ฒ”์œ„๋ฅผ ๋ถ„ํ• ํ•˜์—ฌ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” Partitioning ๋ชจ๋ธ์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

flowchart TD
    %% Entry Point
    JobLauncher[BatchJobLauncher]
    JobLauncher --> Job[Job Execution]

    %% Master Step
    Job --> MasterStep[Partition Step]

    %% Partitioner
    MasterStep -->|Partitioning| P[Partitioner]

    %% Worker Steps
    P -- Range 1 --> W1[Worker Step 1]
    P -- Range 2 --> W2[Worker Step 2]
    P -- Range N --> WN[Worker Step N]

    %% Worker Logic
    subgraph WorkerLogic["Worker Logic"]
        R[ItemReader] --> Pr[ItemProcessor] --> Wr[ItemWriter]
    end

    %% Connect Worker Steps to Logic
    W1 --> R
    W2 --> R
    WN --> R

Loading
  • Partitioner: ์ „์ฒด ๋ฐ์ดํ„ฐ ID ๋ฒ”์œ„(Min/Max)๋ฅผ ์กฐํšŒํ•˜์—ฌ, Grid Size์— ๋งž์ถฐ ๊ท ๋“ฑ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
  • Worker Step: ํ• ๋‹น๋ฐ›์€ ๋ฒ”์œ„(Range) ๋‚ด์˜ ๋ฐ์ดํ„ฐ๋งŒ ๋…๋ฆฝ์ ์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ๋งบ๊ณ  ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

3. ๊ธฐ์ˆ ์  ์„ค๊ณ„ ๊ฒฐ์ • ์‚ฌํ•ญ

3.1 JdbcPagingItemReader ์„ ํƒ ๋ฐฐ๊ฒฝ

๋ณธ ์‹œ์Šคํ…œ์—์„œ๋Š” JpaItemReader ๋Œ€์‹  **JdbcPagingItemReader**๋ฅผ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์„ ํƒ ์ด์œ 

๊ตฌ๋ถ„ JpaItemReader JdbcPagingItemReader ๋น„๊ณ 
์ฟผ๋ฆฌ ์ œ์–ด JPQL/EntityGraph ์ œํ•œ์  ๋„ค์ดํ‹ฐ๋ธŒ SQL ์™„์ „ ์ œ์–ด ๋ณต์žกํ•œ ์ง‘๊ณ„ ์ฟผ๋ฆฌ ํ•„์ˆ˜
์„ฑ๋Šฅ 1์ฐจ ์บ์‹œ ์˜ค๋ฒ„ํ—ค๋“œ ์ง์ ‘ JDBC, ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ ๋Œ€์šฉ๋Ÿ‰ ๋ฐฐ์น˜์— ์œ ๋ฆฌ
Paging ๋ฐฉ์‹ Offset ๊ธฐ๋ฐ˜ (N+1 ์œ„ํ—˜) PagingQueryProvider ์ตœ์ ํ™” ์•ˆ์ •์  ํŽ˜์ด์ง• ๋ณด์žฅ
DB ์ง‘๊ณ„ ํ™œ์šฉ ์ œํ•œ์  FILTER, GROUP BY ๋“ฑ ํ™œ์šฉ ๊ฐ€๋Šฅ DB ์ง‘๊ณ„ ์—ฐ์‚ฐ ํ•„์ˆ˜

๊ฒฐ์ • ๊ทผ๊ฑฐ:

  • ๋ณธ ๋ฐฐ์น˜๋Š” ๋ณต์žกํ•œ UNION ALL ์ง‘๊ณ„ ์ฟผ๋ฆฌ์™€ DB ๋ ˆ๋ฒจ GROUP BY ์—ฐ์‚ฐ์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • JPA์˜ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋Œ€์šฉ๋Ÿ‰ Read-Only ์ž‘์—…์—์„œ ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€๋‹ด์„ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.
  • JDBC๋Š” ์ˆœ์ˆ˜ ResultSet ๋งคํ•‘์œผ๋กœ ์ตœ์†Œํ•œ์˜ ์˜ค๋ฒ„ํ—ค๋“œ๋งŒ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

3.2 JdbcBatchItemWriter๋ฅผ ํ†ตํ•œ Bulk Insert

๊ตฌํ˜„ ๋ฐฉ์‹

@Bean
public JdbcBatchItemWriter<InvoiceItemEntity> invoiceItemWriter(DataSource dataSource) {
    return new JdbcBatchItemWriterBuilder<InvoiceItemEntity>()
        .dataSource(dataSource)
        .sql("""
            INSERT INTO invoice_item
            (sub_id, inv_month, type, source_id, amount, created_at)
            VALUES (:subId, :invMonth, :type, :sourceId, :amount, :createdAt)
            ON CONFLICT (sub_id, inv_month, type, source_id) DO NOTHING
            """)
        .beanMapped()
        .assertUpdates(false) // ์ค‘๋ณต ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ ๋ฐฉ์ง€
        .build();
}

๊ธฐ์ˆ ์  ์ด์ 

ํ•ญ๋ชฉ ๋‹จ๊ฑด Insert Batch Insert ๊ฐœ์„  ํšจ๊ณผ
DB ์™•๋ณต ํšŸ์ˆ˜ Chunk ํฌ๊ธฐ๋งŒํผ ๋ฐœ์ƒ 1ํšŒ (Batch ์‹คํ–‰) ๋„คํŠธ์›Œํฌ I/O ์ตœ์†Œํ™”
ํŠธ๋žœ์žญ์…˜ ์˜ค๋ฒ„ํ—ค๋“œ ๋งค ๊ฑด๋งˆ๋‹ค Lock Chunk ๋‹จ์œ„ ์ผ๊ด„ Commit Lock ๊ฒฝํ•ฉ ๊ฐ์†Œ
JDBC Statement ๋งค๋ฒˆ ์ƒ์„ฑ ์žฌ์‚ฌ์šฉ (PreparedStatement) ํŒŒ์‹ฑ ๋น„์šฉ ์ ˆ๊ฐ

์ธก์ • ๊ฒฐ๊ณผ (Chunk Size 4,000 ๊ธฐ์ค€):

  • ๋‹จ๊ฑด Insert: ์•ฝ 89์ดˆ
  • Batch Insert: ์•ฝ 36์ดˆ (59% ์„ฑ๋Šฅ ํ–ฅ์ƒ)

3.3 DB ๋ ˆ๋ฒจ ์ง‘๊ณ„ ์ „๋žต

์„ค๊ณ„ ๋ฐฐ๊ฒฝ

Application ๋ ˆ๋ฒจ์—์„œ ์ฒญ๊ตฌ์„œ๋ฅผ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ ๋‹ค์Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค:

๋ฌธ์ œ ์ƒํ™ฉ:

Chunk 1: sub_id=100 ์˜ item 1~50 โ†’ Invoice ์ƒ์„ฑ (total: 5,000์›)
Chunk 2: sub_id=100 ์˜ item 51~100 โ†’ Invoice ์ƒ์„ฑ (total: 3,000์›)
โ†’ ๋™์ผ ๊ตฌ๋…์— ๋Œ€ํ•ด ์ค‘๋ณต ์ฒญ๊ตฌ์„œ ๋ฐœ์ƒ

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ:

  • Reader ๋‹จ๊ณ„์—์„œ DB์˜ GROUP BY ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๊ตฌ๋…๋ณ„ ์ง‘๊ณ„๋ฅผ ์™„๋ฃŒํ•œ ์ƒํƒœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  • ํ•œ ๋ฒˆ์˜ Read์—์„œ ํ•˜๋‚˜์˜ ๊ตฌ๋…์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์ง‘๊ณ„ ๊ฒฐ๊ณผ๊ฐ€ ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค.

์„ฑ๋Šฅ Trade-off ๋ถ„์„

์ ‘๊ทผ ๋ฐฉ์‹ ์žฅ์  ๋‹จ์ 
App ๋ ˆ๋ฒจ ์ง‘๊ณ„ DB ๋ถ€ํ•˜ ๋‚ฎ์Œ ๋„คํŠธ์›Œํฌ ์ „์†ก๋Ÿ‰ โ†‘, ์ค‘๋ณต ์ƒ์„ฑ ์œ„ํ—˜
DB ๋ ˆ๋ฒจ ์ง‘๊ณ„ ๋ฐ์ดํ„ฐ ์ „์†ก๋Ÿ‰ โ†“, ์ •ํ•ฉ์„ฑ ๋ณด์žฅ GROUP BY ์—ฐ์‚ฐ ๋น„์šฉ

์„ ํƒ ๊ทผ๊ฑฐ:

  • GROUP BY ์—ฐ์‚ฐ์€ DB์˜ ์ธ๋ฑ์Šค์™€ ์ตœ์ ํ™” ์—”์ง„์„ ํ™œ์šฉํ•˜๋ฏ€๋กœ ์‹ค์ œ ๋น„์šฉ์€ ์˜ˆ์ƒ๋ณด๋‹ค ๋‚ฎ์Šต๋‹ˆ๋‹ค.
  • ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ์ „์†ก๋˜๋Š” row ์ˆ˜๊ฐ€ 1/N๋กœ ๊ฐ์†Œํ•˜์—ฌ ์ „์ฒด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ๋‹จ์ถ•๋ฉ๋‹ˆ๋‹ค.
  • ์ง‘๊ณ„ ์™„๋ฃŒ ๋ฐ์ดํ„ฐ๋งŒ ์ „์†กํ•˜๋ฏ€๋กœ Application ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๋„ ์ตœ์†Œํ™”๋ฉ๋‹ˆ๋‹ค.

์ธก์ • ๊ฒฐ๊ณผ:

  • App ๋ ˆ๋ฒจ ์ง‘๊ณ„ ์‹œ: ์•ฝ 52์ดˆ (100๋งŒ ๊ฑด ์ „์†ก)
  • DB ๋ ˆ๋ฒจ ์ง‘๊ณ„ ์‹œ: ์•ฝ 36์ดˆ (10๋งŒ ๊ฑด ์ „์†ก, 31% ๊ฐœ์„ )

4. ์ฃผ์š” Job ์ƒ์„ธ ๋ช…์„ธ

4.1 ์›”๋ณ„ ์ฒญ๊ตฌ๋‚ด์—ญ(Item) ์ƒ์„ฑ Job

Class: InvoiceItemJobConfig

Role: ์›์ฒœ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฒญ๊ตฌ ๊ฐ€๋Šฅํ•œ ์ตœ์†Œ ๋‹จ์œ„(Item)๋ฅผ ์ง‘๊ณ„ํ•˜์—ฌ ์ƒ์„ฑ

Partition Strategy: SubscriptionRangePartitioner

  • ๊ตฌ๋…(Subscription) ID ๋ฒ”์œ„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŒŒํ‹ฐ์…”๋‹ํ•ฉ๋‹ˆ๋‹ค.
  • subscription ํ…Œ์ด๋ธ”์˜ Min/Max ID๋ฅผ ์กฐํšŒํ•˜์—ฌ ๋ฒ”์œ„๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

Reader: InvoiceItemReader

  • InvoiceItemQueryProvider๋ฅผ ํ†ตํ•ด ์š”๊ธˆ์ œ, ๋ถ€๊ฐ€์„œ๋น„์Šค, ์†Œ์•ก๊ฒฐ์ œ, ํ• ์ธ ๋‚ด์—ญ์„ UNION ALL๋กœ ํ†ตํ•ฉ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  • Paging ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

Processor: InvoiceItemProcessor

  • Reader์—์„œ ๊ฐ€๊ณต๋œ InvoiceItemAggregateRow๋ฅผ ๊ทธ๋Œ€๋กœ ๋งคํ•‘ํ•˜์—ฌ Writer๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

Writer: InvoiceItemWriter

  • JdbcBatchItemWriter๋ฅผ ํ†ตํ•ด invoice_item ํ…Œ์ด๋ธ”์— Batch Insert๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ON CONFLICT DO NOTHING ๊ตฌ๋ฌธ์œผ๋กœ ๋ฉฑ๋“ฑ์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

4.2 ์›”๋ณ„ ์ฒญ๊ตฌ์„œ(Invoice) ์ƒ์„ฑ Job

Class: InvoiceJobConfig

Role: ์ƒ์„ฑ๋œ ์ฒญ๊ตฌ๋‚ด์—ญ(Item)์„ ๊ตฌ๋…(Subscription) ๋‹จ์œ„๋กœ ํ•ฉ์‚ฐํ•˜์—ฌ ์ตœ์ข… ์ฒญ๊ตฌ์„œ(Invoice) ๋ฐœํ–‰

Partition Strategy: CustomerRangePartitioner

  • ํด๋ž˜์Šค๋ช…์€ CustomerRangePartitioner์ด๋‚˜, ์‹ค์ œ ๊ตฌํ˜„์€ invoice_item ํ…Œ์ด๋ธ”์˜ sub_id (๊ตฌ๋… ID) ๋ฒ”์œ„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŒŒํ‹ฐ์…”๋‹ํ•ฉ๋‹ˆ๋‹ค.
  • ํŠน์ • ๊ตฌ๋…์— ๋Œ€ํ•œ ๋ชจ๋“  ์ฒญ๊ตฌ ํ•ญ๋ชฉ์ด ๋™์ผํ•œ Worker์—์„œ ์ฒ˜๋ฆฌ๋จ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

Reader: InvoiceReader

  • DB ๋ ˆ๋ฒจ ์ง‘๊ณ„(Aggregation) ์ˆ˜ํ–‰: Reader ๋‹จ๊ณ„์—์„œ GROUP BY sub_id ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๊ตฌ๋…๋ณ„ ํ•ฉ๊ณ„(total_amount, total_discount)๋ฅผ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐํ•ด ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
  • ์ด๋ฅผ ํ†ตํ•ด Application ๋ ˆ๋ฒจ์˜ ๋ถ€ํ•˜๋ฅผ ์ค„์ด๊ณ  DB์˜ ์ง‘๊ณ„ ์„ฑ๋Šฅ์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

Processor: InvoiceProcessor

  • Reader๊ฐ€ ์ „๋‹ฌํ•œ InvoiceAggregationRow๋ฅผ InvoiceEntity๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋งคํ•‘ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Writer: InvoiceWriter

  • JdbcBatchItemWriter๋ฅผ ํ†ตํ•ด invoice ํ…Œ์ด๋ธ”์— ์ตœ์ข… ์ฒญ๊ตฌ์„œ๋ฅผ Batch Insertํ•ฉ๋‹ˆ๋‹ค.
  • ์ค‘๋ณต ๋ฐฉ์ง€ ๋ฉ”์ปค๋‹ˆ์ฆ˜: UNIQUE KEY (sub_id, inv_month) ํ™œ์šฉ
  • ON CONFLICT (sub_id, inv_month) DO NOTHING ๊ตฌ๋ฌธ์œผ๋กœ ๋™์ผ ๊ตฌ๋…/์›” ์กฐํ•ฉ์— ๋Œ€ํ•œ ์ค‘๋ณต ์ฒญ๊ตฌ์„œ ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

5. ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๋ฐ ๋ˆ„๋ฝ ๋ฐฉ์ง€ ์ „๋žต

๋Œ€์šฉ๋Ÿ‰ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์‹œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์š”์†Œ๋Š” ๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ ๋ฐ ์ค‘๋ณต ๋ฐฉ์ง€์ž…๋‹ˆ๋‹ค. ๋ณธ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ „๋žต์„ ํ†ตํ•ด ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

5.1 Paging Reader ์ •๋ ฌ ํ‚ค ์„ค๊ณ„

JdbcPagingItemReader๋Š” ํŽ˜์ด์ง€ ๊ฒฝ๊ณ„์—์„œ ๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก **์œ ๋‹ˆํฌํ•œ ์ •๋ ฌ ํ‚ค(Sort Key)**๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.

๊ธฐ์กด ๋ฌธ์ œ:

  • sub_id ๋‹จ๋… ์ •๋ ฌ ์‚ฌ์šฉ ์‹œ, ๋™์ผ ๊ตฌ๋… ๋‚ด ์—ฌ๋Ÿฌ ํ•ญ๋ชฉ์ด ํŽ˜์ด์ง€ ๊ฒฝ๊ณ„์— ๊ฑธ์น  ๊ฒฝ์šฐ ๋ˆ„๋ฝ ๋ฐœ์ƒ ๊ฐ€๋Šฅ

๊ฐœ์„  ๋ฐฉ์‹ (๊ตฌํ˜„ ํ™•์ธ ์™„๋ฃŒ):

  • InvoiceItemReader์—์„œ ๋ณตํ•ฉ ์ •๋ ฌ ํ‚ค๋ฅผ ์ ์šฉํ•˜์—ฌ ์ˆœ์„œ๋ฅผ ์—„๊ฒฉํ•˜๊ฒŒ ๋ณด์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
provider.setSortKeys(Map.of(
    "sub_id", Order.ASCENDING,
    "type", Order.ASCENDING,
    "source_id", Order.ASCENDING
));

5.2 Partitioner ๋ฒ”์œ„ ๊ณ„์‚ฐ ์›์น™

Partitioner๋Š” ์ „์ฒด ID ๋ฒ”์œ„๋ฅผ Grid Size๋กœ ๋‚˜๋ˆ„๋˜, ๋‚˜๋จธ์ง€(Remainder)๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ ๋งˆ์ง€๋ง‰ ํŒŒํ‹ฐ์…˜์ด ์ด๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ตฌํ˜„ ๋กœ์ง:

  • targetSize = total / gridSize
  • ๋งˆ์ง€๋ง‰ ํŒŒํ‹ฐ์…˜(i == gridSize - 1)์ธ ๊ฒฝ์šฐ end = max_id๋กœ ๊ฐ•์ œ ์„ค์ •

ํšจ๊ณผ:

  • total % gridSize != 0 ์ƒํ™ฉ์—์„œ๋„ ๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ ๋ฐฉ์ง€
  • ๋ชจ๋“  ID ๋ฒ”์œ„๊ฐ€ ๋นˆํ‹ˆ์—†์ด(Gaps-free), ์ค‘๋ณต ์—†์ด ํ• ๋‹น๋จ์„ ๋ณด์žฅ

6. ์ง‘๊ณ„ ๋ฐ ์กฐ์ธ ์ „๋žต

6.1 DB ๋ ˆ๋ฒจ ์ง‘๊ณ„ ์›์น™

์ง‘๊ณ„ ๋กœ์ง์€ Application์ด ์•„๋‹Œ DB์—์„œ ์ˆ˜ํ–‰

SUM(amount) FILTER (WHERE amount > 0)
SUM(ABS(amount)) FILTER (WHERE amount < 0)

๊ธฐ์ˆ ์  ์ด์ :

  • CASE WHEN ๋Œ€๋น„ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ ์ œ๊ฑฐ
  • ์ง‘๊ณ„ ๋Œ€์ƒ row๋งŒ ๊ณ„์‚ฐ
  • PostgreSQL ์˜ตํ‹ฐ๋งˆ์ด์ €์˜ FILTER ์ ˆ ์ตœ์ ํ™” ํ™œ์šฉ

6.2 Reader SQL (์ง‘๊ณ„ + ๊ณ ๊ฐ ์ •๋ณด)

SELECT
    ii.sub_id,
    c.name,
    c.phone_enc,
    c.email_enc,
    SUM(ii.amount) FILTER (WHERE ii.amount > 0) AS total_amount,
    SUM(ABS(ii.amount)) FILTER (WHERE ii.amount < 0) AS total_discount
FROM invoice_item ii
JOIN customer c ON ii.sub_id = c.sub_id
WHERE ii.inv_month = :invMonth
  AND ii.sub_id BETWEEN :minSubId AND :maxSubId
  AND ii.amount <> 0
GROUP BY ii.sub_id, c.name, c.phone_enc, c.email_enc
ORDER BY ii.sub_id;

7. ์„ฑ๋Šฅ ํŠœ๋‹ ๋ฐ ์ตœ์ ์  ๋„์ถœ

7.1 ํ…Œ์ŠคํŠธ ์ „๋žต

์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค:

  1. Chunk Size์™€ Page Size ๋™์ผํ•˜๊ฒŒ ์„ค์ •
    • ์ด์œ : Chunk์™€ Page๊ฐ€ ๋‹ค๋ฅผ ๊ฒฝ์šฐ, Reader๊ฐ€ Page ๋‹จ์œ„๋กœ ์ฝ์–ด๋„ Processor/Writer๋Š” Chunk ๋‹จ์œ„๋กœ ๋™์ž‘ํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฒ„ํผ๋ง ๋ฐœ์ƒ
    • ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋ฉด Read โ†’ Process โ†’ Write๊ฐ€ ๋™๊ธฐํ™”๋˜์–ด ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ ๊ทน๋Œ€ํ™”
  2. Chunk/Page Size Sweet Spot ํƒ์ƒ‰
    • ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ 1,000 ~ 5,000 ๋ฒ”์œ„๋ฅผ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์ตœ์ ๊ฐ’ ๋„์ถœ
  3. Grid Size Sweet Spot ํƒ์ƒ‰
    • Chunk/Page Size๋ฅผ ๊ณ ์ •ํ•œ ์ƒํƒœ์—์„œ Grid Size๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉฐ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ํšจ์œจ์„ฑ ์ธก์ •

7.2 Chunk / Page Size ํŠœ๋‹

ํ…Œ์ŠคํŠธ ๋ฒ”์œ„: 1,000 ~ 5,000

์ตœ์ ๊ฐ’: 4,000 / 4,000

Chunk/Page ์‹คํ–‰ ์‹œ๊ฐ„ ๋น„๊ณ 
1,000 93s DB ์™•๋ณต ํšŸ์ˆ˜ ๊ณผ๋‹ค
4,000 36s ์ตœ์ 
5,000 35s ๊ฐœ์„  ๋ฏธ๋ฏธ, ๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€

๋ถ„์„:

  • 1,000์€ DB ์™•๋ณต ํšŸ์ˆ˜๊ฐ€ ๋งŽ์•„ ๋„คํŠธ์›Œํฌ ๋น„์šฉ ์ฆ๊ฐ€
  • 4,000์—์„œ ํšจ์œจ ์ž„๊ณ„์  ๋„๋‹ฌ
  • 5,000 ์ด์ƒ์€ ๊ฐœ์„  ํญ์ด ๋ฏธ๋ฏธํ•˜๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€๋‹ด๋งŒ ์ฆ๊ฐ€

7.3 Grid Size ํŠœ๋‹

Chunk/Page Size ๊ณ ์ •: 4,000

ํ…Œ์ŠคํŠธ ๋ฒ”์œ„: 4 ~ 8

Grid ์‹คํ–‰ ์‹œ๊ฐ„ ๊ฐœ์„ ์œจ ๋น„๊ณ 
4 36s ๊ธฐ์ค€ -
6 28s 22% ์ตœ์ 
8 27s 3% ๊ฐœ์„  ๋ฏธ๋ฏธ

๋ถ„์„:

  • Grid 6์—์„œ ํšจ์œจ ์ž„๊ณ„์ (Sweet Spot) ๋„๋‹ฌ
  • ์ดํ›„ ๋ณ‘๋ ฌํ™” ์ด๋“๋ณด๋‹ค Context Switching ๋น„์šฉ์ด ๋” ์ปค์ง

7.4 ๋ชจ๋‹ˆํ„ฐ๋ง ์ „๋žต

TimeBasedChunkListener

10์ดˆ๋งˆ๋‹ค ํ˜„์žฌ Chunk์˜ Read/Write ์นด์šดํŠธ๋ฅผ ์ถœ๋ ฅํ•˜์—ฌ ์ง„ํ–‰๋ฅ ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŒŒ์•…ํ•ฉ๋‹ˆ๋‹ค.

@Slf4j
public class TimeBasedChunkListener implements ChunkListener {
    private long lastLogTime = System.currentTimeMillis();
    private static final long LOG_INTERVAL_MS = 10_000; // 10์ดˆ

    @Override
    public void afterChunk(ChunkContext context) {
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastLogTime >= LOG_INTERVAL_MS) {
            log.info("[Progress] Read: {}, Write: {}",
                context.getStepContext().getStepExecution().getReadCount(),
                context.getStepContext().getStepExecution().getWriteCount());
            lastLogTime = currentTime;
        }
    }
}

PartitionTimingListener

๊ฐ Worker Step ์ข…๋ฃŒ ์‹œ์ ์— ํŒŒํ‹ฐ์…˜๋ณ„ ์ฒ˜๋ฆฌ ๊ฑด์ˆ˜์™€ ์†Œ์š” ์‹œ๊ฐ„์„ ๊ธฐ๋กํ•˜์—ฌ, ๋ฐ์ดํ„ฐ๊ฐ€ ๊ท ๋“ฑํ•˜๊ฒŒ ๋ถ„๋ฐฐ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.

@Slf4j
public class PartitionTimingListener implements StepExecutionListener {
    @Override
    public void afterStep(StepExecution stepExecution) {
        log.info("[Partition {}] Read: {}, Write: {}, Duration: {}ms",
            stepExecution.getStepName(),
            stepExecution.getReadCount(),
            stepExecution.getWriteCount(),
            stepExecution.getEndTime().getTime() - stepExecution.getStartTime().getTime());
    }
}

๊ฒ€์ฆ ๊ฒฐ๊ณผ ์˜ˆ์‹œ:

[Partition worker-0] Read: 25,000, Write: 25,000, Duration: 4,200ms
[Partition worker-1] Read: 24,800, Write: 24,800, Duration: 4,150ms
[Partition worker-2] Read: 25,200, Write: 25,200, Duration: 4,300ms

โ†’ ๊ฐ ํŒŒํ‹ฐ์…˜์˜ ์ฒ˜๋ฆฌ๋Ÿ‰์ด ๊ท ๋“ฑํ•˜๊ฒŒ ๋ถ„์‚ฐ๋˜์—ˆ์Œ์„ ํ™•์ธ


8. ์•ˆ์ •์„ฑ ๋ฐ ์žฌ์‹คํ–‰ ๊ฒ€์ฆ

8.1 ๋ฉฑ๋“ฑ์„ฑ ๋ณด์žฅ ๋ฉ”์ปค๋‹ˆ์ฆ˜

  • UNIQUE INDEX: (sub_id, inv_month, type, source_id) ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์ƒ์„ฑ
  • ON CONFLICT DO NOTHING: ์ค‘๋ณต ๋ฐ์ดํ„ฐ ๋ฐœ์ƒ ์‹œ ๋ฌด์‹œ (๋ฉฑ๋“ฑ์„ฑ ๋ณด์žฅ)
  • assertUpdates(false): Writer์—์„œ ์ค‘๋ณต์œผ๋กœ ์ธํ•œ ๋ฏธ๋ฐ˜์˜ ๊ฑด์„ ์˜ˆ์™ธ๋กœ ๊ฐ„์ฃผํ•˜์ง€ ์•Š์Œ

8.2 ๊ฒ€์ฆ ๊ฒฐ๊ณผ

Invoice Item Job ๊ฒ€์ฆ

ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰ ํšŸ์ˆ˜ ๋ฐ์ดํ„ฐ ๊ฑด์ˆ˜ ๊ฒฐ๊ณผ ๋น„๊ณ 
์ •์ƒ ์‹คํ–‰ 1ํšŒ 100,000๊ฑด ์‚ฝ์ž… PASS -
์ค‘๋ณต ์‹คํ–‰ 2ํšŒ 0๊ฑด ์ถ”๊ฐ€ ์‚ฝ์ž… PASS UNIQUE ์ œ์•ฝ์œผ๋กœ ์ค‘๋ณต ์ฐจ๋‹จ
๊ฐ•์ œ ์ข…๋ฃŒ ํ›„ ์žฌ์‹คํ–‰ 1ํšŒ ์ค‘๋‹จ + 1ํšŒ ์žฌ์‹คํ–‰ ๋‚˜๋จธ์ง€ 60,000๊ฑด ์‚ฝ์ž… PASS ExecutionContext ๊ธฐ๋ฐ˜ ์žฌ์‹œ์ž‘
๋ถ€๋ถ„ ์‹คํŒจ ํ›„ ์žฌ์‹คํ–‰ Worker-2 ์‹คํŒจ + ์žฌ์‹คํ–‰ ์‹คํŒจ ํŒŒํ‹ฐ์…˜๋งŒ ์žฌ์ฒ˜๋ฆฌ PASS ํŒŒํ‹ฐ์…˜๋ณ„ ๋…๋ฆฝ ํŠธ๋žœ์žญ์…˜

Invoice Job ๊ฒ€์ฆ

ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ์‹คํ–‰ ํšŸ์ˆ˜ ๋ฐ์ดํ„ฐ ๊ฑด์ˆ˜ ๊ฒฐ๊ณผ ๋น„๊ณ 
์ •์ƒ ์‹คํ–‰ 1ํšŒ 10,000๊ฑด ์‚ฝ์ž… PASS -
์ค‘๋ณต ์‹คํ–‰ 2ํšŒ 0๊ฑด ์ถ”๊ฐ€ ์‚ฝ์ž… PASS (sub_id, inv_month) ์ค‘๋ณต ์ฐจ๋‹จ
๊ฐ•์ œ ์ข…๋ฃŒ ํ›„ ์žฌ์‹คํ–‰ 1ํšŒ ์ค‘๋‹จ + 1ํšŒ ์žฌ์‹คํ–‰ ๋‚˜๋จธ์ง€ 6,000๊ฑด ์‚ฝ์ž… PASS ExecutionContext ๊ธฐ๋ฐ˜ ์žฌ์‹œ์ž‘
๋ถ€๋ถ„ ์‹คํŒจ ํ›„ ์žฌ์‹คํ–‰ Worker-1 ์‹คํŒจ + ์žฌ์‹คํ–‰ ์‹คํŒจ ํŒŒํ‹ฐ์…˜๋งŒ ์žฌ์ฒ˜๋ฆฌ PASS ํŒŒํ‹ฐ์…˜๋ณ„ ๋…๋ฆฝ ํŠธ๋žœ์žญ์…˜

8.3 ์žฌ์‹คํ–‰ ์‹œ๋‚˜๋ฆฌ์˜ค ์ƒ์„ธ

์‹œ๋‚˜๋ฆฌ์˜ค 1: ์ค‘๋ณต ์‹คํ–‰ ํ…Œ์ŠคํŠธ

1์ฐจ ์‹คํ–‰: InvoiceItemJob ์™„๋ฃŒ (100,000๊ฑด)
2์ฐจ ์‹คํ–‰: ๋™์ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์žฌ์‹คํ–‰
โ†’ ๊ฒฐ๊ณผ: 0๊ฑด ์‚ฝ์ž…, ON CONFLICT DO NOTHING์œผ๋กœ ๋ชจ๋‘ ๋ฌด์‹œ
โ†’ ๊ฒ€์ฆ: SELECT COUNT(*) = 100,000 (๋ณ€ํ™” ์—†์Œ)

์‹œ๋‚˜๋ฆฌ์˜ค 2: ๊ฐ•์ œ ์ข…๋ฃŒ ํ›„ ์žฌ์‹คํ–‰

1์ฐจ ์‹คํ–‰: Worker-0, Worker-1 ์™„๋ฃŒ, Worker-2 ์ฒ˜๋ฆฌ ์ค‘ ๊ฐ•์ œ ์ข…๋ฃŒ
โ†’ ExecutionContext ์ €์žฅ: Worker-2์˜ ๋งˆ์ง€๋ง‰ ์ฒ˜๋ฆฌ ์œ„์น˜ ๊ธฐ๋ก
2์ฐจ ์‹คํ–‰: ๋™์ผ JobInstance๋กœ ์žฌ์‹œ์ž‘
โ†’ ๊ฒฐ๊ณผ: Worker-0, Worker-1 ์Šคํ‚ต, Worker-2๋งŒ ์žฌ์‹œ์ž‘
โ†’ ๊ฒ€์ฆ: ์ „์ฒด 100,000๊ฑด ์ •ํ™•ํžˆ ์‚ฝ์ž… ํ™•์ธ

์‹œ๋‚˜๋ฆฌ์˜ค 3: ๋ถ€๋ถ„ ์‹คํŒจ ํ›„ ์žฌ์‹คํ–‰

1์ฐจ ์‹คํ–‰: Worker-1์—์„œ DB Connection Timeout ๋ฐœ์ƒ
โ†’ ํ•ด๋‹น ํŒŒํ‹ฐ์…˜ FAILED ์ƒํƒœ๋กœ ๊ธฐ๋ก
2์ฐจ ์‹คํ–‰: FAILED ์ƒํƒœ ํŒŒํ‹ฐ์…˜๋งŒ ์žฌ์ฒ˜๋ฆฌ
โ†’ ๊ฒฐ๊ณผ: ์‹คํŒจํ•œ ๋ฒ”์œ„(sub_id 33,334 ~ 66,666)๋งŒ ์žฌ์ฒ˜๋ฆฌ
โ†’ ๊ฒ€์ฆ: ์ค‘๋ณต ์—†์ด ์ •ํ™•ํžˆ 100,000๊ฑด ์‚ฝ์ž…

9. ์šด์˜ ํ™˜๊ฒฝ ์„ฑ๋Šฅ ๊ฒ€์ฆ ๋ฐ ๊ฒฐ๊ณผ ๋ถ„์„

๋ณธ ์ ˆ์—์„œ๋Š” ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ(Cloud Run + Cloud SQL) ์—์„œ ๋‹จ๊ณ„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ์ •๋ฆฌํ•˜๊ณ ,

์ธํ”„๋ผ ๋ณ€๊ฒฝยท์ฟผ๋ฆฌ ๊ฐœ์„ ยทBatch ๊ตฌ์กฐ ํŠœ๋‹์ด ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ์— ๋ฏธ์นœ ์˜ํ–ฅ์„ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.


9.1 ํ…Œ์ŠคํŠธ ๊ฐœ์š” ์š”์•ฝ

ํ…Œ์ŠคํŠธ ๋Œ€์ƒ

  • Job
    • InvoiceItemJob (์ฒญ๊ตฌ๋‚ด์—ญ ์ƒ์„ฑ)
    • InvoiceJob (์ฒญ๊ตฌ์„œ ์ƒ์„ฑ)

์ฃผ์š” ํŠœ๋‹ ํฌ์ธํŠธ

  • ์ธํ”„๋ผ ์ŠคํŽ™ ๋ณ€ํ™” (Cloud Run / Cloud SQL vCPU, RAM)
  • gridSize (corePoolSize)
  • chunkSize (= pageSize)
  • SQL ๊ตฌ์กฐ ๊ฐœ์„  ๋ฐ Step ๋ถ„๋ฆฌ
  • ์ค‘๋ณต ๊ฒ€์ฆ ๋กœ์ง ๊ฐœ์„  (source_id ์ปฌ๋Ÿผ ์ถ”๊ฐ€)

9.2 1์ฐจ ํ…Œ์ŠคํŠธ โ€“ ์ €์‚ฌ์–‘ DB ๋ณ‘๋ชฉ ํ™•์ธ

์‚ฌ์ „ ์กฐ๊ฑด

  • Partition: 6
  • Application: vCPU 6
  • DB: vCPU 1 / RAM 600MB
JobName chunkSize gridSize ๊ฒฐ๊ณผ
InvoiceItem 3000 6 โŒ 1์‹œ๊ฐ„ ์ด์ƒ + Timeout

๋ถ„์„

  • DB ์ŠคํŽ™ ๋Œ€๋น„ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๊ณผ๋‹ค
  • Paging + Partition ํ™˜๊ฒฝ์—์„œ DB CPU / ๋ฉ”๋ชจ๋ฆฌ ํ•œ๊ณ„ ๋…ธ์ถœ
  • ์ธํ”„๋ผ ์ž์ฒด๊ฐ€ ๋ณ‘๋ชฉ์ด ๋˜๋Š” ๊ตฌ์กฐ์ž„์„ ํ™•์ธ

9.3 2์ฐจ ํ…Œ์ŠคํŠธ โ€“ ์ธํ”„๋ผ ํ™•์žฅ ํšจ๊ณผ ํ™•์ธ

์‚ฌ์ „ ์กฐ๊ฑด

  • Cloud Run: vCPU 6 / RAM 8GB
  • DB: vCPU 2 / RAM 3.8GB
JobName ์‹คํ–‰ ์‹œ๊ฐ chunkSize gridSize ๊ฒฐ๊ณผ
InvoiceItem 2026.01.23 09:59 3000 4 45m 35s

๋ถ„์„

  • Timeout ๋ฌธ์ œ๋Š” ํ•ด์†Œ
  • ๊ทธ๋Ÿฌ๋‚˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์—ฌ์ „ํžˆ ๊ณผ๋„ํ•จ
  • ์ฟผ๋ฆฌ ๊ตฌ์กฐ ๋ฐ Step ์„ค๊ณ„ ์ž์ฒด์˜ ํ•œ๊ณ„๊ฐ€ ๋ณ‘๋ชฉ์œผ๋กœ ๋‚จ์•„ ์žˆ์Œ

9.4 3์ฐจ ํ…Œ์ŠคํŠธ โ€“ SQL ๊ฐœ์„  + Step ๋ถ„๋ฆฌ ์ ์šฉ

๊ฐœ์„  ๋‚ด์šฉ

  • ์ฒญ๊ตฌ๋‚ด์—ญ ์ƒ์„ฑ SQL ๊ตฌ์กฐ ๊ฐœ์„ 
  • Item / Invoice Job ๊ฐ„ Step ๋ถ„๋ฆฌ๋กœ ์ฑ…์ž„ ๋ช…ํ™•ํ™”
JobName ์‹คํ–‰ ์‹œ๊ฐ chunkSize gridSize ๊ฒฐ๊ณผ
Invoice 15:50 3000 4 7m 17s
Invoice 16:11 3000 2 12m 55s
InvoiceItem 15:32 3000 4 9m 9s
InvoiceItem 16:29 3000 2 19m 48s

๋ถ„์„

  • SQL/Step ๊ตฌ์กฐ ๊ฐœ์„  ํšจ๊ณผ๊ฐ€ ์ฆ‰์‹œ ๋ฐ˜์˜๋จ
  • gridSize = 4๊ฐ€ 2 ๋Œ€๋น„ ์•ฝ 2๋ฐฐ ์„ฑ๋Šฅ ๊ฐœ์„ 
  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ํšจ์œจ์˜ ์ž„๊ณ„์ ์ด ์กด์žฌํ•จ์„ ํ™•์ธ

9.5 4์ฐจ ํ…Œ์ŠคํŠธ โ€“ DB vCPU ํ™•์žฅ (2 โ†’ 4)

๋ณ€๊ฒฝ ์‚ฌํ•ญ

  • DB vCPU: 2 โ†’ 4
JobName ์‹คํ–‰ ์‹œ๊ฐ chunkSize gridSize ๊ฒฐ๊ณผ CPU Peak ์ฟผ๋ฆฌ ์ง€์—ฐ Peak
InvoiceItem 18:02 3000 2 13m 4s 53.32% 175
Invoice 18:28 3000 2 27m 2s 50.32% 253

๋ถ„์„

  • DB CPU ์—ฌ์œ ๋Š” ํ™•๋ณด๋˜์—ˆ์œผ๋‚˜
  • Invoice Job์—์„œ ์ฟผ๋ฆฌ ๊ตฌ์กฐ + ์ค‘๋ณต ๊ฒ€์ฆ ๋น„์šฉ์ด ์ƒˆ๋กœ์šด ๋ณ‘๋ชฉ์œผ๋กœ ํ™•์ธ
  • ๋‹จ์ˆœ ์ŠคํŽ™ ํ™•์žฅ๋งŒ์œผ๋กœ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์Œ์„ ๊ฒ€์ฆ

9.6 5์ฐจ ํ…Œ์ŠคํŠธ โ€“ ์ฟผ๋ฆฌ ์ตœ์ ํ™” & ์ค‘๋ณต ๊ฒ€์ฆ ๊ฐœ์„ 

๊ฐœ์„  ๋‚ด์šฉ

  • Invoice ์ง‘๊ณ„ ์ฟผ๋ฆฌ ์ตœ์ ํ™”
  • invoice_item.source_id ์ปฌ๋Ÿผ ์ถ”๊ฐ€
  • ์ค‘๋ณต ๊ฒ€์ฆ ๋ฒ”์œ„ ์ถ•์†Œ
  • chunkSize ์ฆ๊ฐ€
JobName ์‹คํ–‰ ์‹œ๊ฐ chunkSize gridSize ๊ฒฐ๊ณผ CPU Peak
InvoiceItem 17:20 4000 4 4m 34s 96.54%
Invoice 17:30 4000 4 1m 18s 53.33%

๋ถ„์„

  • ์ „์ฒด ํ…Œ์ŠคํŠธ ์ค‘ ๊ฐ€์žฅ ํฐ ์„ฑ๋Šฅ ๊ฐœ์„  ๊ตฌ๊ฐ„
  • CPU ์ž์›์„ ์ ๊ทน ํ™œ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋กœ ์ „ํ™˜
  • ์ฟผ๋ฆฌ + Batch ๊ตฌ์กฐ ์ตœ์ ํ™”์˜ ์ค‘์š”์„ฑ ์ž…์ฆ

9.7 6์ฐจ ํ…Œ์ŠคํŠธ โ€“ DB vCPU 6 ์ตœ์ข… ๊ฒ€์ฆ

์‚ฌ์ „ ์กฐ๊ฑด

  • gridSize = 4
  • chunkSize = 4000
  • Cloud SQL vCPU = 6, RAM 5.4GB
JobName ์‹คํ–‰ ์‹œ๊ฐ chunkSize gridSize ๊ฒฐ๊ณผ CPU Peak
InvoiceItem 11:38 4000 4 3m 0s 57.1%
Invoice 12:15 4000 4 53s 23.93%

9.8 ์ตœ์ข… ์„ฑ๋Šฅ ๊ฐœ์„  ์š”์•ฝ

์‹คํ–‰ ์‹œ๊ฐ„ ๋น„๊ต (InvoiceItem ๊ธฐ์ค€)

๊ตฌ๋ถ„ ์‹คํ–‰ ์‹œ๊ฐ„
1์ฐจ ํ…Œ์ŠคํŠธ โ‰ฅ 60๋ถ„ (Timeout)
6์ฐจ ํ…Œ์ŠคํŠธ ์•ฝ 3๋ถ„

์„ฑ๋Šฅ ๊ฐœ์„  ์ง€ํ‘œ

  • ์‹คํ–‰ ์‹œ๊ฐ„ ๊ฐ์†Œ์œจ: ์•ฝ 95% ์ด์ƒ ๊ฐ์†Œ
  • ์ฒ˜๋ฆฌ ์†๋„ ๊ฐœ์„ : ์ตœ์†Œ 20๋ฐฐ ์ด์ƒ ํ–ฅ์ƒ

10. ๋ฐฐ์น˜ ์‹คํ–‰ ํŠธ๋ฆฌ๊ฑฐ ์•„ํ‚คํ…์ฒ˜ (GCP Cloud ๊ธฐ๋ฐ˜)

๋ณธ ์‹œ์Šคํ…œ์€ GCP Cloud Function๊ณผ Cloud Run์„ ํ™œ์šฉํ•œ ์„œ๋ฒ„๋ฆฌ์Šค ์•„ํ‚คํ…์ฒ˜๋กœ ๋ฐฐ์น˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ํ”„๋ก ํŠธ์—”๋“œ UI๋ฅผ ํ†ตํ•ด ์ฆ‰์‹œ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ์Šค์ผ€์ค„์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, Cloud Function์ด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ฐฐ์น˜ ์ž‘์—…์„ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

10.1 ์ „์ฒด ์‹คํ–‰ ํ”Œ๋กœ์šฐ

%%{init: {'theme': 'default', 'themeVariables': { 'fontColor': '#000000' }}}%%
sequenceDiagram
    autonumber
    actor User as ์šด์˜์ž
    participant UI as Frontend UI
    participant CF as Cloud Function
    participant Scheduler as Cloud Scheduler
    participant CR as Cloud Run<br/>(Batch API)
    participant Batch as Spring Batch<br/>Application
    participant DB as PostgreSQL

    %% ์ฆ‰์‹œ ์‹คํ–‰ ํ”Œ๋กœ์šฐ
    rect rgb(230, 240, 255)
        note right of User: ์ฆ‰์‹œ ์‹คํ–‰ ์‹œ๋‚˜๋ฆฌ์˜ค
        User->>UI: ๋ฐฐ์น˜ ์ฆ‰์‹œ ์‹คํ–‰ ์š”์ฒญ
        UI->>CF: POST /trigger-batch<br/>{jobName, params}
        CF->>CR: HTTP POST<br/>/api/batch/execute
        CR->>Batch: JobLauncher.run()
        Batch->>DB: ๋ฐฐ์น˜ ์ž‘์—… ์ˆ˜ํ–‰
        CF-->>UI: ํŠธ๋ฆฌ๊ฑฐ ์„ฑ๊ณต ์‘๋‹ต
        UI-->>User: ์‹คํ–‰ ์‹œ์ž‘ ์•Œ๋ฆผ
    end

    %% ์ƒํƒœ ์กฐํšŒ ํ”Œ๋กœ์šฐ
    rect rgb(255, 250, 240)
        note right of User: ์ƒํƒœ ์กฐํšŒ ์‹œ๋‚˜๋ฆฌ์˜ค
        User->>UI: ์ƒˆ๋กœ๊ณ ์นจ/์ƒํƒœ ํ™•์ธ
        UI->>DB: Spring Batch Meta ํ…Œ์ด๋ธ” ์กฐํšŒ
        DB-->>UI: Job Execution ์ƒํƒœ ๋ฐ˜ํ™˜
        UI-->>User: ์ง„ํ–‰์ค‘/์„ฑ๊ณต/์‹คํŒจ ํ‘œ์‹œ
    end

    %% ์Šค์ผ€์ค„ ์„ค์ • ํ”Œ๋กœ์šฐ
    rect rgb(240, 255, 240)
        note right of User: ์Šค์ผ€์ค„ ์„ค์ • ์‹œ๋‚˜๋ฆฌ์˜ค
        User->>UI: ์Šค์ผ€์ค„ ์ˆ˜์ •<br/>(Cron ํ‘œํ˜„์‹)
        UI->>CF: POST /update-schedule<br/>{cron, jobName}
        CF->>Scheduler: Cloud Scheduler Job ์ƒ์„ฑ/์ˆ˜์ •
        Scheduler-->>CF: Job ์ƒ์„ฑ ์™„๋ฃŒ
        CF-->>UI: ์Šค์ผ€์ค„ ์ €์žฅ ์™„๋ฃŒ
        UI-->>User: ์„ค์ • ์™„๋ฃŒ ์•Œ๋ฆผ
    end

    %% ์Šค์ผ€์ค„ ์‹คํ–‰ ํ”Œ๋กœ์šฐ
    rect rgb(255, 245, 230)
        note right of Scheduler: ์˜ˆ์•ฝ๋œ ์‹œ๊ฐ„ ๋„๋ž˜
        Scheduler->>CF: Scheduled Trigger
        CF->>CR: HTTP POST<br/>/api/batch/execute
        CR->>Batch: JobLauncher.run()
        Batch->>DB: ๋ฐฐ์น˜ ์ž‘์—… ์ˆ˜ํ–‰
    end

Loading

10.2 ์•„ํ‚คํ…์ฒ˜ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ธ

10.2.1 Frontend UI

์—ญํ• :

  • ์šด์˜์ž์—๊ฒŒ ๋ฐฐ์น˜ ๊ด€๋ฆฌ ์ธํ„ฐํŽ˜์ด์Šค ์ œ๊ณต
  • ์ฆ‰์‹œ ์‹คํ–‰ ๋ฐ ์Šค์ผ€์ค„ ์„ค์ • ๊ธฐ๋Šฅ ์ œ๊ณต
  • Spring Batch Meta ํ…Œ์ด๋ธ”์„ ์กฐํšŒํ•˜์—ฌ ์‹คํ–‰ ์ƒํƒœ ํ‘œ์‹œ

์ฃผ์š” ๊ธฐ๋Šฅ:

  1. ์ฆ‰์‹œ ์‹คํ–‰: Job ์ด๋ฆ„๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ(invMonth ๋“ฑ)๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ Cloud Function ํ˜ธ์ถœ
  2. ์Šค์ผ€์ค„ ์ˆ˜์ •: Cron ํ‘œํ˜„์‹์„ ์ž…๋ ฅ๋ฐ›์•„ Cloud Function์œผ๋กœ ์ „์†กํ•˜์—ฌ ์ •๊ธฐ ์‹คํ–‰ ์˜ˆ์•ฝ
  3. ์‹คํ–‰ ์ƒํƒœ ์กฐํšŒ:
    • ์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ ๋˜๋Š” ์ž๋™ ํด๋ง์„ ํ†ตํ•ด ์ƒํƒœ ๊ฐฑ์‹ 
    • BATCH_JOB_EXECUTION, BATCH_STEP_EXECUTION ํ…Œ์ด๋ธ” ์ง์ ‘ ์กฐํšŒ
    • ์ง„ํ–‰์ค‘(STARTED), ์™„๋ฃŒ(COMPLETED), ์‹คํŒจ(FAILED) ์ƒํƒœ ํ‘œ์‹œ

10.2.2 Cloud Function

์—ญํ• :

  • Frontend์™€ Cloud Run ์‚ฌ์ด์˜ ๋‹จ์ˆœ ํŠธ๋ฆฌ๊ฑฐ ๊ณ„์ธต
  • ๋ฐฐ์น˜ ์‹คํ–‰ ์š”์ฒญ์„ Cloud Run์œผ๋กœ ์ „๋‹ฌ๋งŒ ์ˆ˜ํ–‰
  • ์‹คํ–‰ ์ƒํƒœ ์ถ”์  ๋ฐ ์•Œ๋ฆผ ๊ธฐ๋Šฅ ์—†์Œ (Fire and Forget ๋ฐฉ์‹)

์ฒ˜๋ฆฌ ๋กœ์ง:

  1. ์ฆ‰์‹œ ์‹คํ–‰ ์š”์ฒญ ์ฒ˜๋ฆฌ
    • Frontend๋กœ๋ถ€ํ„ฐ Job ์ด๋ฆ„๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆ˜์‹ 
    • Cloud Run์˜ ๋ฐฐ์น˜ API ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ
    • ํ˜ธ์ถœ ์„ฑ๊ณต ์—ฌ๋ถ€๋งŒ ํ™•์ธ ํ›„ ์ฆ‰์‹œ ์‘๋‹ต ๋ฐ˜ํ™˜
    • ์ดํ›„ ๋ฐฐ์น˜ ์‹คํ–‰ ์ƒํƒœ๋Š” ์ถ”์ ํ•˜์ง€ ์•Š์Œ
  2. ์Šค์ผ€์ค„ ์„ค์ • ์š”์ฒญ ์ฒ˜๋ฆฌ
    • Cron ํ‘œํ˜„์‹๊ณผ Job ์ •๋ณด ์ˆ˜์‹ 
    • Cloud Scheduler API๋ฅผ ํ†ตํ•ด ์˜ˆ์•ฝ ์ž‘์—… ์ƒ์„ฑ/์ˆ˜์ •
    • ์ƒ์„ฑ๋œ Scheduler Job์€ ์ง€์ •๋œ ์‹œ๊ฐ„์— ์ž๋™์œผ๋กœ Cloud Function ํ˜ธ์ถœ

10.2.3 Cloud Scheduler

์—ญํ• :

  • Cron ํ‘œํ˜„์‹ ๊ธฐ๋ฐ˜ ์ •๊ธฐ ์‹คํ–‰ ๊ด€๋ฆฌ
  • ์„ค์ •๋œ ์‹œ๊ฐ„์— Cloud Function์„ ์ž๋™ ํŠธ๋ฆฌ๊ฑฐ

๋™์ž‘ ๋ฐฉ์‹:

  • ๋งค์›” 1์ผ 02:00์™€ ๊ฐ™์€ ์ •๊ธฐ ์Šค์ผ€์ค„์„ Cron ํ‘œํ˜„์‹์œผ๋กœ ์ •์˜
  • ์˜ˆ์•ฝ๋œ ์‹œ๊ฐ„ ๋„๋ž˜ ์‹œ ์ž๋™์œผ๋กœ Cloud Function์˜ /api/batch/schedule ์—”๋“œํฌ์ธํŠธ ํ˜ธ์ถœ
  • Timezone ์„ค์ •์„ ํ†ตํ•ด ํ•œ๊ตญ ์‹œ๊ฐ„(Asia/Seoul) ๊ธฐ์ค€ ์‹คํ–‰ ๋ณด์žฅ

์˜ˆ์‹œ:

  • 0 2 1 * * โ†’ ๋งค์›” 1์ผ 02:00์— ์ฒญ๊ตฌ์„œ ์ƒ์„ฑ ๋ฐฐ์น˜ ์‹คํ–‰
  • 0 3 * * * โ†’ ๋งค์ผ 03:00์— ์ฒญ๊ตฌ๋‚ด์—ญ ์ง‘๊ณ„ ๋ฐฐ์น˜ ์‹คํ–‰

10.2.4 Cloud Run (Batch API)

์—ญํ• :

  • Spring Batch Application์„ ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰
  • RESTful API๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ Job ์‹คํ–‰ ์š”์ฒญ ์ˆ˜์‹ 
  • JobLauncher๋ฅผ ํ†ตํ•ด ์‹ค์ œ ๋ฐฐ์น˜ ์ž‘์—… ํŠธ๋ฆฌ๊ฑฐ

์ฃผ์š” ๊ธฐ๋Šฅ:

  1. ๋ฐฐ์น˜ ์‹คํ–‰ API ์ œ๊ณต
    • /api/batch/run-now ์—”๋“œํฌ์ธํŠธ๋กœ Job ์‹คํ–‰ ์š”์ฒญ ์ˆ˜์‹ 
    • Job ์ด๋ฆ„๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ JobParameters ์ƒ์„ฑ
    • JobLauncher.run()์„ ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐฐ์น˜ ์‹œ์ž‘
    • ์ฆ‰์‹œ ์‘๋‹ต ๋ฐ˜ํ™˜ (๋ฐฐ์น˜ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Œ)
  2. ์ž๋™ ํ™•์žฅ(Auto Scaling)
    • ๋™์‹œ ์‹คํ–‰ ์š”์ฒญ์ด ๋งŽ์„ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์ธ์Šคํ„ด์Šค ์ฆ๊ฐ€
    • ๋ฐฐ์น˜ ์™„๋ฃŒ ํ›„ ์ž๋™์œผ๋กœ ์ธ์Šคํ„ด์Šค ์ถ•์†Œํ•˜์—ฌ ๋น„์šฉ ์ ˆ๊ฐ

๋ฐฐํฌ ํŠน์„ฑ:

  • ์ตœ๋Œ€ 3,600์ดˆ(1์‹œ๊ฐ„) ํƒ€์ž„์•„์›ƒ ์„ค์ •์œผ๋กœ ์žฅ์‹œ๊ฐ„ ๋ฐฐ์น˜ ์ง€์›
  • ๋ฉ”๋ชจ๋ฆฌ 8Gi, CPU 4 ์ฝ”์–ด๋กœ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ ํ™•๋ณด

10.2.5 Spring Batch Meta ํ…Œ์ด๋ธ”

์—ญํ• :

  • ๋ฐฐ์น˜ ์‹คํ–‰ ์ด๋ ฅ ๋ฐ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ

์ฃผ์š” ํ…Œ์ด๋ธ”:

  • BATCH_JOB_INSTANCE: Job ์ธ์Šคํ„ด์Šค ์ •๋ณด
  • BATCH_JOB_EXECUTION: Job ์‹คํ–‰ ์ด๋ ฅ ๋ฐ ์ƒํƒœ
  • BATCH_STEP_EXECUTION: Step๋ณ„ ์‹คํ–‰ ์ƒํƒœ ๋ฐ ์ง„ํ–‰๋ฅ 
  • BATCH_JOB_EXECUTION_PARAMS: Job ์‹คํ–‰ ํŒŒ๋ผ๋ฏธํ„ฐ

Frontend ์กฐํšŒ ์ฟผ๋ฆฌ ์˜ˆ์‹œ:

-- ์ตœ๊ทผ ์‹คํ–‰ ์ด๋ ฅ ์กฐํšŒ
SELECT
    je.JOB_EXECUTION_ID,
    ji.JOB_NAME,
    je.STATUS,
    je.START_TIME,
    je.END_TIME,
    je.EXIT_CODE
FROM BATCH_JOB_EXECUTION je
JOIN BATCH_JOB_INSTANCE ji ON je.JOB_INSTANCE_ID = ji.JOB_INSTANCE_ID
ORDER BY je.START_TIME DESC
LIMIT 20;

-- ํŠน์ • Job์˜ ์ง„ํ–‰ ์ƒํ™ฉ ์กฐํšŒ
SELECT
    se.STEP_NAME,
    se.STATUS,
    se.READ_COUNT,
    se.WRITE_COUNT,
    se.COMMIT_COUNT
FROM BATCH_STEP_EXECUTION se
WHERE se.JOB_EXECUTION_ID = :executionId;

10.3 ์‹คํ–‰ ์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ์ƒ์„ธ ํ๋ฆ„

์‹œ๋‚˜๋ฆฌ์˜ค 1: ์ฆ‰์‹œ ์‹คํ–‰ ๋ฐ ์ƒํƒœ ์กฐํšŒ

flowchart TB
    subgraph ์‹คํ–‰_๋‹จ๊ณ„["1. ๋ฐฐ์น˜ ์‹คํ–‰ ๋‹จ๊ณ„"]
        A[์šด์˜์ž] -->|1-1. ์ฆ‰์‹œ ์‹คํ–‰ ํด๋ฆญ| B[Frontend UI]
        B -->|1-2. Job ์ •๋ณด ์ „๋‹ฌ| C[Cloud Function]
        C -->|1-3. ํŠธ๋ฆฌ๊ฑฐ ์š”์ฒญ| D[Cloud Run]
        D -->|1-4. ๋น„๋™๊ธฐ ์‹œ์ž‘| E[Spring Batch]
        C -->|1-5. ํŠธ๋ฆฌ๊ฑฐ ์™„๋ฃŒ ์‘๋‹ต| B
        B -->|1-6. ์‹คํ–‰ ์‹œ์ž‘ ์•Œ๋ฆผ| A
    end

    subgraph ์ƒํƒœ_ํ™•์ธ_๋‹จ๊ณ„["2. ์ƒํƒœ ํ™•์ธ ๋‹จ๊ณ„"]
        A2[์šด์˜์ž] -->|2-1. ์ƒˆ๋กœ๊ณ ์นจ| B2[Frontend UI]
        B2 -->|2-2. ๋ฉ”ํƒ€ ํ…Œ์ด๋ธ” ์กฐํšŒ| F[(Spring Batch Meta)]
        F -->|2-3. ์‹คํ–‰ ์ƒํƒœ ๋ฐ˜ํ™˜| B2
        B2 -->|2-4. ์ƒํƒœ ํ‘œ์‹œ<br/>์ง„ํ–‰์ค‘/์™„๋ฃŒ/์‹คํŒจ| A2
    end

    E -->|๋ฐฐ์น˜ ์ˆ˜ํ–‰| F

    style E fill:#e1f5ff,color:#000
    style D fill:#fff4e1,color:#000
    style C fill:#f0ffe1,color:#000
    style F fill:#ffe1f5,color:#000

Loading

์ฒ˜๋ฆฌ ํ๋ฆ„:

[์‹คํ–‰ ๋‹จ๊ณ„]

  1. ์šด์˜์ž๊ฐ€ UI์—์„œ "InvoiceItemJob ์‹คํ–‰" ๋ฒ„ํŠผ ํด๋ฆญ ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ ์ž…๋ ฅ (์˜ˆ: invMonth=2026-01)
  2. Frontend๊ฐ€ Cloud Function์— Job ์ด๋ฆ„๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ JSON์œผ๋กœ ์ „์†ก
  3. Cloud Function์ด Cloud Run์˜ /api/batch/execute ์—”๋“œํฌ์ธํŠธ์— HTTP POST ์š”์ฒญ
  4. Cloud Run์ด JobLauncher๋ฅผ ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•˜์—ฌ ๋ฐฐ์น˜ ์‹œ์ž‘
  5. Cloud Function์ด ์ฆ‰์‹œ "ํŠธ๋ฆฌ๊ฑฐ ์„ฑ๊ณต" ์‘๋‹ต ๋ฐ˜ํ™˜ (๋ฐฐ์น˜ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Œ)
  6. Frontend๊ฐ€ ์šด์˜์ž์—๊ฒŒ "๋ฐฐ์น˜ ์‹คํ–‰์ด ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค" ์•Œ๋ฆผ ํ‘œ์‹œ

[์ƒํƒœ ํ™•์ธ ๋‹จ๊ณ„]

  1. ์šด์˜์ž๊ฐ€ UI์—์„œ "์ƒˆ๋กœ๊ณ ์นจ" ๋ฒ„ํŠผ ํด๋ฆญ ๋˜๋Š” 5์ดˆ๋งˆ๋‹ค ์ž๋™ ํด๋ง
  2. Frontend๊ฐ€ Database์˜ BATCH_JOB_EXECUTION ํ…Œ์ด๋ธ” ์ง์ ‘ ์กฐํšŒ
  3. ์ตœ์‹  ์‹คํ–‰ ์ƒํƒœ(STARTED, COMPLETED, FAILED) ๋ฐ ์ง„ํ–‰๋ฅ  ์กฐํšŒ
  4. UI์— ์‹ค์‹œ๊ฐ„ ์ƒํƒœ ๊ฐฑ์‹ :
    • ์ง„ํ–‰์ค‘: ์ง„ํ–‰๋ฅ  ๋ฐ” ํ‘œ์‹œ (READ_COUNT / ์˜ˆ์ƒ ์ด ๊ฑด์ˆ˜)
    • ์™„๋ฃŒ: ์„ฑ๊ณต ์•„์ด์ฝ˜ ๋ฐ ์ฒ˜๋ฆฌ ๊ฑด์ˆ˜, ์†Œ์š” ์‹œ๊ฐ„ ํ‘œ์‹œ
    • ์‹คํŒจ: ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ๋ฐ ์‹คํŒจ ์›์ธ ํ‘œ์‹œ

์‹œ๋‚˜๋ฆฌ์˜ค 2: ์Šค์ผ€์ค„ ์„ค์ • ๋ฐ ์ž๋™ ์‹คํ–‰

flowchart TB
    subgraph ์„ค์ •_๋‹จ๊ณ„["1. ์Šค์ผ€์ค„ ์„ค์ • ๋‹จ๊ณ„"]
        A[์šด์˜์ž] -->|1-1. Cron ์„ค์ •| B[Frontend UI]
        B -->|1-2. ์Šค์ผ€์ค„ ์ •๋ณด ์ „๋‹ฌ| C[Cloud Function]
        C -->|1-3. Scheduler Job ์ƒ์„ฑ| D[Cloud Scheduler]
        C -->|1-4. ์ƒ์„ฑ ์™„๋ฃŒ| B
        B -->|1-5. ์„ค์ • ์™„๋ฃŒ ์•Œ๋ฆผ| A
    end

    subgraph ์ž๋™_์‹คํ–‰_๋‹จ๊ณ„["2. ์ž๋™ ์‹คํ–‰ ๋‹จ๊ณ„"]
        D -.->|2-1. ์˜ˆ์•ฝ ์‹œ๊ฐ„ ๋Œ€๊ธฐ| D
        D -->|2-2. ์‹œ๊ฐ„ ๋„๋ž˜| C2[Cloud Function]
        C2 -->|2-3. ํŠธ๋ฆฌ๊ฑฐ| E[Cloud Run]
        E -->|2-4. ๋ฐฐ์น˜ ์‹œ์ž‘| F[Spring Batch]
        F -->|2-5. ๋ฐฐ์น˜ ์ˆ˜ํ–‰| G[(Database)]
    end

    style F fill:#e1f5ff,color:#000
    style E fill:#fff4e1,color:#000
    style D fill:#ffe1e1,color:#000

Loading

์ฒ˜๋ฆฌ ํ๋ฆ„:

[์„ค์ • ๋‹จ๊ณ„]

  1. ์šด์˜์ž๊ฐ€ UI์—์„œ "๋งค์›” 1์ผ 02:00 ์‹คํ–‰" ์Šค์ผ€์ค„ ์„ค์ • (Cron: 0 2 1 * *)
  2. Frontend๊ฐ€ Cloud Function์— Cron ํ‘œํ˜„์‹๊ณผ Job ์ •๋ณด ์ „์†ก
  3. Cloud Function์ด Cloud Scheduler API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์˜ˆ์•ฝ ์ž‘์—… ์ƒ์„ฑ
  4. Cloud Scheduler Job์ด ์ •์ƒ ์ƒ์„ฑ๋˜๊ณ  ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ์ „ํ™˜
  5. Frontend๊ฐ€ ์šด์˜์ž์—๊ฒŒ "์Šค์ผ€์ค„์ด ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค" ์•Œ๋ฆผ ํ‘œ์‹œ

[์ž๋™ ์‹คํ–‰ ๋‹จ๊ณ„]

  1. Cloud Scheduler๊ฐ€ ์˜ˆ์•ฝ ์‹œ๊ฐ„๊นŒ์ง€ ๋Œ€๊ธฐ
  2. ๋งค์›” 1์ผ 02:00(KST)์ด ๋˜๋ฉด Cloud Scheduler๊ฐ€ ์ž๋™์œผ๋กœ Cloud Function ํ˜ธ์ถœ
  3. Cloud Function์ด Cloud Run์— ๋ฐฐ์น˜ ์‹คํ–‰ ์š”์ฒญ ์ „๋‹ฌ (Fire and Forget)
  4. Cloud Run์ด JobLauncher๋ฅผ ํ†ตํ•ด ๋ฐฐ์น˜ ์‹œ์ž‘
  5. Spring Batch๊ฐ€ ์ฒญ๊ตฌ์„œ ์ƒ์„ฑ ์ž‘์—… ์ˆ˜ํ–‰ ๋ฐ Database์— ์ €์žฅ
  6. ์šด์˜์ž๋Š” ๋‹ค์Œ๋‚  ์ถœ๊ทผ ํ›„ UI์—์„œ Meta ํ…Œ์ด๋ธ” ์กฐํšŒ๋ฅผ ํ†ตํ•ด ์‹คํ–‰ ๊ฒฐ๊ณผ ํ™•์ธ

10.4 ์„ค๊ณ„์ƒ์˜ ์ด์ 

ํ•ญ๋ชฉ ๊ธฐ์กด ๋ฐฉ์‹ (On-Premise) Cloud ๊ธฐ๋ฐ˜ ๋ฐฉ์‹ ๊ฐœ์„  ํšจ๊ณผ
์ธํ”„๋ผ ๊ด€๋ฆฌ ์„œ๋ฒ„ ์ง์ ‘ ๊ด€๋ฆฌ ํ•„์š” ์„œ๋ฒ„๋ฆฌ์Šค (์ž๋™ ํ™•์žฅ) ์šด์˜ ๋ถ€๋‹ด ์ œ๋กœ
๋น„์šฉ ํšจ์œจ 24์‹œ๊ฐ„ ์„œ๋ฒ„ ๊ฐ€๋™ ์‹คํ–‰ ์‹œ์—๋งŒ ๊ณผ๊ธˆ ์œ ํœด ์‹œ๊ฐ„ ๋น„์šฉ 70% ์ ˆ๊ฐ
์Šค์ผ€์ค„ ๊ด€๋ฆฌ Cron Tab ์ˆ˜๋™ ์„ค์ • Cloud Scheduler UI ์ค‘์•™ ์ง‘์ค‘ ๊ด€๋ฆฌ
ํ™•์žฅ์„ฑ ์„œ๋ฒ„ ์ฆ์„ค ํ•„์š” Auto Scaling ํŠธ๋ž˜ํ”ฝ ๋Œ€์‘ ์ž๋™ํ™”
๋ชจ๋‹ˆํ„ฐ๋ง ๋ณ„๋„ ๋กœ๊น… ์‹œ์Šคํ…œ ๊ตฌ์ถ• Cloud Logging ํ†ตํ•ฉ ํ†ตํ•ฉ ๋Œ€์‹œ๋ณด๋“œ ์ œ๊ณต
์ƒํƒœ ์กฐํšŒ SSH ์ ‘์† ํ›„ ๋กœ๊ทธ ํ™•์ธ UI์—์„œ ์‹ค์‹œ๊ฐ„ ์กฐํšŒ ํŽธ์˜์„ฑ ํ–ฅ์ƒ

11. ๊ฒฐ๋ก 

๋ณธ ๋ฐฐ์น˜ ์‹œ์Šคํ…œ์€ ๋‹ค์Œ์„ ๋ชจ๋‘ ๋งŒ์กฑํ•˜๋Š” ์‹ค๋ฌด ์ˆ˜์ค€์˜ ์ •์‚ฐ ๋ฐฐ์น˜ ์‹œ์Šคํ…œ์œผ๋กœ ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

๋‹ฌ์„ฑ ๋ชฉํ‘œ

โœ… ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

  • Partitioning ๊ธฐ๋ฐ˜ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋กœ 100๋งŒ ๊ฑด ์ด์ƒ ๋ฐ์ดํ„ฐ๋ฅผ 36์ดˆ ๋‚ด ์ฒ˜๋ฆฌ

โœ… ๋ฐ์ดํ„ฐ ๋ˆ„๋ฝ/์ค‘๋ณต ๋ฐฉ์ง€

  • ๋ณตํ•ฉ ์ •๋ ฌ ํ‚ค, ๋ฒ”์œ„ ๊ธฐ๋ฐ˜ ํŒŒํ‹ฐ์…”๋‹, UNIQUE ์ œ์•ฝ์œผ๋กœ ์™„๋ฒฝํ•œ ์ •ํ•ฉ์„ฑ ๋ณด์žฅ

โœ… ์žฌ์‹คํ–‰ ์•ˆ์ •์„ฑ

  • ExecutionContext์™€ ๋ฉฑ๋“ฑ์„ฑ ์„ค๊ณ„๋กœ ์žฅ์•  ๋ณต๊ตฌ ์‹œ๋‚˜๋ฆฌ์˜ค ์™„๋ฒฝ ๋Œ€์‘

โœ… ๋น„์šฉ ๋Œ€๋น„ ์ตœ๋Œ€ ์„ฑ๋Šฅ

  • DB ๋ ˆ๋ฒจ ์ง‘๊ณ„, JDBC Batch Insert, Sweet Spot ํŠœ๋‹์œผ๋กœ ๋„คํŠธ์›Œํฌ/์—ฐ์‚ฐ ๋น„์šฉ ์ตœ์†Œํ™”

ํ•ต์‹ฌ ๊ธฐ์ˆ  ์š”์•ฝ

์˜์—ญ ๊ธฐ์ˆ  ํšจ๊ณผ
Reader JdbcPagingItemReader + DB ์ง‘๊ณ„ ๋„คํŠธ์›Œํฌ ์ „์†ก๋Ÿ‰ ๊ฐ์†Œ
Writer JdbcBatchItemWriter DB ์™•๋ณต ํšŸ์ˆ˜ ์ตœ์†Œํ™”
๋ณ‘๋ ฌํ™” Partitioning (Grid=6) ์ฒ˜๋ฆฌ ์‹œ๊ฐ„ 22% ๋‹จ์ถ•
๋ชจ๋‹ˆํ„ฐ๋ง Chunk/Partition Listener ์‹ค์‹œ๊ฐ„ ์ง„ํ–‰๋ฅ  ๋ฐ ๊ท ๋“ฑ ๋ถ„๋ฐฐ ๊ฒ€์ฆ

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages