Ma'lumotlarni Backfill qilish uchun resurslarni ajratish strategiyasi

Ma'lumotlarni Backfill qilish uchun resurslarni ajratish strategiyasi

1. Kirish: Katta hajmdagi ma'lumotlarni qayta uzatish (Backfill) arxitekturasining muammolari

Zamonaviy taqsimlangan tizim arxitekturasida ma'lumotlar pipeline'ining barqarorligi xizmatning hayotiyligi bilan bog'liq. Xususan, narsalar interneti (IoT), moliya yoki real vaqt monitoring tizimlarida yuzaga keladigan vaqt qatorlari (TimeSeries) ma'lumotlari soniyaning o'zida minglab tranzaktsiyalarni talab etadi. Bunday muhitda tizim nosozligi, tarmoq uzilishi yoki pastki oqim (Downstream) ma'lumotlar bazasining vaqtincha ishdan chiqishi tufayli muvaffaqiyatsiz ma'lumotlarni hech qanday yo'qotmasdan qayta to'ldirish jarayoni backend muhandislari uchun juda qiyin vazifadir.

Ko'p dasturchilar tomonidan qilinadigan xatolardan biri muvaffaqiyatsiz ma'lumotlarni oddiygina aylantirib yuborish yoki real vaqt trafik oqimi davomida umumiy xabarlar navbatiga yuborishdir. Biroq, yirik operatsion muhitda bunday yondashuv real vaqt xizmatini nishonlaydigan kasallangan qochish (Cascading Failure)ni keltirib chiqaradi.

Ushbu texnik hujjatda yuqori tezlikdagi xabarlar infratuzilmasi NATS va Java'ning yuqori samarali no-bloklangan ma'lumotlar tuzilmasi ConcurrentLinkedQueue, hamda Spring'ning maxsus asinxron ip to'plami (@Async) organik ravishda birlashtirilishi orqali, real vaqt trafikini mukammal himoya qilgan holda millionlab to'plangan vaqt qatorlari ma'lumotlarini barqaror va samarali qayta uzatgan tizimni takomillashtirish tajribasi va uning optimallashtirish strategiyasini batafsil bayon etamiz.

2. Mavjud arxitekturaning cheklov va texnik muammolari (Pain Point)

Takomillashtirishdan oldingi dastlabki tizim bitta xabarlar pipeline'ida real vaqt trafikini va qayta uzatish trafikini bir vaqtda qabul qiluvchi sodda tuzilma edi. Bu tuzilma kichik test muhitida kamchiliklarni keltirib chiqarmagan, ammo operatsion muhitda yirik backfill ishga tushganda quyidagi uchta halokatli torli tugunlarni keltirib chiqardi.

2.1. Resurs tugashi (Resource Exhaustion) tufayli real vaqt xizmatining ishdan chiqishi

Real vaqt foydalanuvchi so'rovlari va backfill so'rovlari umumiy HTTP ip to'plami va bitta xabarlar navbatida resurslarni bo'lishdi. Natijada millionlab backfill ma'lumotlari kirganda, qayta uzatish ishchilar ipi butun CPU, xotira va ip resurslarini egallab oldi. Oqibatda real vaqtida kirishi kerak bo'lgan asosiy so'rovlar ip bera olmasdan cheksiz kutish sharoitida qoldi va Connection Timeout halokatiga olib keldi.

image1.png

Rasm 1. Real vaqt so'rovlari va backfill so'rovlari bir xil resurslarni bo'lishganda yuzaga keladigan resurs tugashi muammosi

2.2. Vaqt asosidagi (Time-based) rejalashtirishdagi yuk ko‘tarishning oshishi

Navbatga yigilgan backfill ma'lumotlarini pastki oqim ma'lumotlar bazasiga (PostgreSQL) yuklaganingizda, dastlab "Har 10 soniyada rejalashtiruvchi ishini boshqarasiz, shunda navbatni to'liq bo'shatadi" kabi vaqt markazli logikadan foydalanilgan. Bu ma'lumot kirishida noaniqlikni ko'paytiradi. Ma'lum bir 10 soniya ichida ma'lumot kutilmaganda to'planishi natijasida, keyingi rejalashtirish vaqtida minglab so'rovlar bir vaqtning o'zida ma'lumotlar bazasiga yuboriladi va bog'lanish havzasi ishdan chiqadi hamda DB CPU foydalanish darajasi 100% ga yetadi, 'trafik bo'roni (Spike)' hodisasi tez-tez yuz berar edi.

2.3. Ipni bloklash (Thread Blocking) tufayli resurslarning behuda sarfi

Backfill ma'lumotlari qayta uzatishning barchasi yakunlangandan so'ng, yakuniy statistika to'plash (Aggregation) jarayonini ishga tushirishda "Barcha ma'lumotlar tegishli bo’lganda bir oz kuting" degan ruxsatda Thread.sleep() yoki CountDownLatch kabi sinkron guruh bilan bloklash mexanizmi ishlatilgan. Bu kutish holatidagi ipni xotiraga to'sishiga olib keladi va tez-tez kontekstni o'zgartirish (Context Switching) holatini keltirib chiqaradi hamda umumiy tizim resurs samaradorligini nihoyatda yomonlashtiradi.

3. Izolyatsiya va optimizatsiya uchun 4 ta arxitektura takomillashtirish strategiyalari

Yuqoridagi uchta cheklovni bartaraf etish maqsadida, infratuzilma qatlamidan ariza ma'lumotlar tuzilmasi va ip modeligacha bo'lgan umumiy tuzilmani to'liq ajratish uchun Bulkhead uslubi (devor izolyatsiyasi arxitekturasi) joriy etildi.

3.1. Infra va ma'lumotlar tuzilishi qatlamini izolyatsiya qilish: NATS va ConcurrentLinkedQueue

Birinchi navbatda, trafikni to'liq yo'naltirishni ajratish ustida ish qilishni boshladik. Haqiqiy vaqt ma'lumotlar mavzusi va orqa to'ldirish uchun mo'ljallangan mavzularni infra (NATS) darajasida ajratdik va Java ilovasi ichida orqa to'ldirish ma'lumotlarini xavfsiz qabul qilish uchun mustaqil xotira buferi qatorini @Bean sifatida ro'yxatdan o'tkazdik.

@Configuration
public class QueueConfig {

    @Bean
    public Queue<TimeSeriesVo> backfillTimeSeriesVoQueue() {
        // 다중 스레드 환경에서 Lock-Free 고성능을 보장하는 논블로킹 큐 선택
        return new ConcurrentLinkedQueue<>();
    }
}

Bu erda asosiy narsa ConcurrentLinkedQueue tanlashida. Oddiy LinkedBlockingQueue yoki ArrayBlockingQueue ishlab chiqaruvchi (Producer) va iste'molchi (Consumer) iplar qatoriga kirganda, blokirovka (Lock) olishlari kerak bo'lgani uchun, bir vaqtning o'zida raqobat qichqiriqlari paydo bo'ladi. Aksincha, ConcurrentLinkedQueue blokirovkani ishlatmaydi va CPU darajasida atomik operatsiyalar asosida CAS (Compare-And-Swap) algoritmi bilan ishlaydi. Tarmoq javob tezligi juda tez bo'lgan NATS iste'molchisi soniyada minglab ma'lumotlarni qo'shsa ham, ishchi iplar bilan to'qnashmasdan no-blokirovka (Non-blocking) tarzda ma'lumotlarni xotiraga qabul qiladigan kuyov rahbari sifatida ishlaydi.

image2.png

Rasm 2. Bir nechta NATS iste'molchi iplar CAS algoritmi bilan blokirovkasiz qatorga ma'lumotlarni yuklash usuli

3.2. Ip qatlamini izolyatsiya qilish: @Async("backfillTimeSeriesPool")

NATS va xotira qatori orqali ma'lumotlarni xavfsiz olib bo'lsa-da, ularni chiqarib, asl biznes mantiqini bajaruvchi va DB ga yuklaydigan ishchilar veb-serverning asosiy iplar to'plamidan foydalanayotgan bo'lsa, yuqorida keltirilgan resurs tugashi muammosi hal qilinmaydi.

Shuning uchun, Spring'ning @Async mexanizmini ishlatib, orqa to'ldirish ishlariga ixtisoslashgan mustaqil maxsus Thread Pool yaratdik.

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean(name = "backfillTimeSeriesPool")
    public Executor backfillTimeSeriesPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);          // 기본 유지할 스레드 개수
        executor.setMaxPoolSize(10);          // 트래픽 폭발 시 최대 확장 스레드 개수
        executor.setQueueCapacity(500);       // Task 대기 큐 용량
        executor.setThreadNamePrefix("backfill-task-");
        executor.setRejectedExecutionHandler(
            new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

Shunday qilib, izolyatsiyalangan iplar to'plamni e'lon qilib, orqa to'ldirishni amalga oshiruvchi asinxron ishchi komponentga biriktiramiz.

@Component
public class BackfillWorker {

    private final Queue<TimeSeriesVo> backfillTimeSeriesVoQueue;
    private final TimeSeriesRepository timeSeriesRepository;

    public BackfillWorker(
            Queue<TimeSeriesVo> backfillTimeSeriesVoQueue,
            TimeSeriesRepository timeSeriesRepository) {
        this.backfillTimeSeriesVoQueue = backfillTimeSeriesVoQueue;
        this.timeSeriesRepository = timeSeriesRepository;
    }

    @Async("backfillTimeSeriesPool")
    public void processBackfill() {
        // 공용 HTTP 스레드를 전혀 오염시키지 않고, 백필 전용 스레드(backfill-task-) 내에서만 안전하게 루프 구동
        List<TimeSeriesVo> chunkList = new ArrayList<>();
        while (!backfillTimeSeriesVoQueue.isEmpty()) {
            TimeSeriesVo vo = backfillTimeSeriesVoQueue.poll();
            if (vo != null) {
                chunkList.add(vo);
                // 3.3 섹션에서 서술할 크기 기반 청크 처리 로직이 여기에 위치함
                if (chunkList.size() >= 1000) {
                    timeSeriesRepository.saveAll(chunkList);
                    chunkList.clear();
                }
            }
        }
        if (!chunkList.isEmpty()) {
            timeSeriesRepository.saveAll(chunkList);
        }
    }
}

Ushbu loyiha orqali orqa to'ldirish ishlarining bir necha soat davomida og'ir orqada ishlashiga qaramay, foydalanuvchilarni haqiqiy vaqt rejimida talablarini bajaruvchi http-nio-iplar to'plamlari butunlay izolyatsiyalangan holda 0.1 soniyadan kam tezlikda yuqori samaradorlikda javob berish imkoniyatini saqlab qoldik.

3.3. Backend oqim nazorati (Flow Control): o'lchamga asoslangan (Size-based) bo'laklarni chiqarish

Vaqtga asoslangan rejalashtiruvchi tomonidan DB ga to'siq yuborish muammosini hal qilish uchun, tizimning cheklangan quvvati ichida optimal tezlikni saqlash maqsadida o'lchamga asoslangan Extraction (o'lchovga asoslangan chiqarish) usuliga o'tdik.

Ip qatoridan ma'lumotlarni olishda vaqt oqimiga bog'lanmasdan faqat ma'lumotlar soniga (Chunk Size) to'g'ridan-to'g'ri qaratiladi. Ma'lumotlar bazasi (PostgreSQL) indeksni yangilanishi va ulanish yukini hisobga olib, eng barqaror qabul qilishi mumkin bo'lgan optimal bo'lak hajmini belgilab, qator ma'lumotlar to'ldirilgani sari belgilangan hajmda bo'laklab, doimo DB ga yuboriladi (poll()).

Ushbu oqim nazorati orqali orqa to'ldirish ma'lumotlari yuz minglab miqdorda tezda kirganida ham, ma'lumotlar bazasi bir vaqtning o'zida muzlab qolishining eng yomon holatini oldini olish va tizim cheklari doirasida yukning yuqori chegarasini nazorat qilib, asta-sekin bo'lak qo'shish (Bulk Insert) ishini tugatish uchun struktural barqarorlikni ta'minladik.

image3.png

Rasm 3. Vaqtga asoslangan rejalashtirish yuk ko'tarilishni keltirib chiqaradi, ammo o'lchamga asoslangan bo'lak chiqarish aniq ishlov berishni saqlaydi

3.4. Asinxron resurslarni optimallashtirish: nazorat flaglari va isEmpty() no-blokirovka qadriyatlari

Orqaga to'liq qayta uzatilgan ma'lumotlar orqali, tizim yakuniy ma'lumotlarning to'g'riligini ta'minlash uchun agregatsiya (Aggregation) amallarini bajarishi kerak. Mavjud ipni to'xtatish vositasini olib tashlab, mukammal noan'anaviy oqimni amalga oshirish uchun Control Flag (boshqaruv bayrogi) va navbat holatini tekshirishni birlashtirdik.

NATS protokoli orqali ma'lumot uzatishning oxiri haqida xabar beruvchi orqaga to'ldirish tugatish signali (End-of-Stream Flag) iste'molchiga yetganida, tizim holat bayrog'ini COMPLETING ga o'zgartiradi. Keyin ishchi iplar uxlay olmaydi yoki kutmaydi, no-blokirovka holatida asosiy navbat holatini tekshiradi.

public void checkAndTriggerAggregation() {
    // 스레드를 재우는 오버헤드 없이, 락 프리로 큐의 공백 상태를 체크
    if (isEndOfStreamSignalReceived && backfillTimeSeriesVoQueue.isEmpty()) {
        // 대기 시간 0초 만에 즉시 최종 집계 프로세스 비동기 트리거
        aggregationService.triggerFinalAggregation();
    }
}

backfillTimeSeriesVoQueue.isEmpty() true qaytargan darhol, har qanday CPU kechikishlari yoki iplar bo'sh holatlarisiz yakuniy agregatsiya logikasi ishga tushiriladi.

Bunga qo'shimcha ravishda, tarqatilgan noan'anaviy muhit xususiyatlari tufayli tarmoq kechikishi natijasida 'orqaga to'ldirish ma'lumotlari so'rovi' va 'muvaffaqiyatli javob olish' navbatining o'zgarishi bilan bog'liq ba'zi poyga sharoitlarini oldini olish uchun, maxsus normal javob hodisasi qabul qilinganda, uni xotira ichida oq ro'yxatga (Whitelist) birinchi bo'lib ro'yxatga olish va o'zaro tasdiqlash logikasini qo'shib, ma'lumotlarni yo'qotish imkoniyatini kamaytirdik.

4. Xulosa: Infratuzilma va dasturiy ta'minotning uyg'unligi o'ziga xos saboqlar beradi

Ushbu yuqori darajadagi loyiha orqali olingan eng qimmatli saboq shundaki, "Qancha tez yuqori samarali tarqatilgan infratuzilma (NATS) joriy etilsa, hamda bu chegarani qabul qiladigan orqa tomondagi dastur ichidagi ma'lumot tuzilmalari va ip modelining, oqim nazorat strategiyalarining yomon holati bo'lsa, tor joylar joyni o'zgartirib, oxir-oqibat yana paydo bo'ladi".

Infratuzilma darajasida mavzularni ajratish, Spring sozlamalari orqali @Async maxsus ip havzalarini izolyatsiya qilish, ConcurrentLinkedQueue orqali qulfsiz (Lock-Free) buferlash, va hajmga asoslangan bo'laklarni boshqarish zanjiri organik ravishda harakatga o'tganda, nihoyat haqiqiy ma'nodagi katta hajmdagi ma'lumot quvur liniyasi shakllanadi.

Tizimning resurslar sarfini dramatik ravishda kamaytirish bilan bir qatorda, keng ko'lamli qayta uzatish muhitida ma'lumotlar yaxlitligini to'liq taminlagan ushbu arxitektura yaxshilanish tajribasi cheklangan hisoblash resurslarida tadbirlar o'rtasida sotishning mukofoti (Trade-off)ni muvozanatlashga va har qanday ekstremal trafik sharoitlarida ham chaynalmasdan mustahkam tizim barqarorligini ta'minlashga asos bo'ldi.

Pancake Maker

Site footer