-Eski bulk jo‘natma ma’lumotlarini qabul qilish bosqichini sinxron qayta ishlashdan saqlash va chiqish strukturasiga o‘tkazish tajribasi-
1. Kirish — Biz qanday muammoga duch keldik?
Men mas’ul bo‘lgan adapter xizmati shifoxonaning eski tibbiy axborot tizimi (bundan keyin — eski tizim) yuborgan so‘rovnoma jo‘natma ma’lumotlarini qabul qiluvchi va ularni ichki mikroxizmatlarda (account xizmati va survey xizmati) ro‘yxatdan o‘tkazuvchi shlyuz vazifasini bajaradi. Bir qarashda, u bitta xabarni qabul qilib ro‘yxatdan o‘tkazadigan oddiy integratsiyaga o‘xshaydi, ammo ishlab chiqarish muhitiga chiqarilgach, uchta muammo bir vaqtning o‘zida yuzaga chiqdi.
-
Qayta ishlash vaqti eski tizim tranzaksiyasining bandlik vaqtiga bevosita teng edi.Bitta qabul qilingan elementni qayta ishlash uchun adapter eski tizimga sinxron ravishda ikki marta, account xizmatiga to‘rt yoki besh marta, survey xizmatiga esa taxminan o‘n marta murojaat qiladi — jami qariyb 20 ta REST round trip. Eski tizim bulk elementlar uchun o‘z tranzaksiyasi doirasida ketma-ket murojaatlar yuborgani sababli, adapter qayta ishlash vaqtining butun davomida tranzaksiyani band qilib turardi. Trassalarni tekshirganimda, har bir element uchun HTTP javob vaqti 1,4–2,2 soniya ekanini, bir vaqtning o‘zida yuzlab element kelgan davrlarda esa eski tizim tomonidagi timeoutlar doimiy xavf bo‘lganini ko‘rdim.
-
Qisman xatolar yetim resurslarni qoldirardi.Adapter o‘z ma’lumotlar bazasisiz (DB-less) ishlab chiqilgani sababli, kirish nuqtasiga biriktirilgan @Transactional amalda hech qanday ish bajarmasdi. Agar oraliq REST chaqiruvi xato bilan tugasa, avvalgi bosqichlarda yaratilgan mehmon akkauntlari yoki qabul qiluvchi ma’lumotlari rollback qilinmasdan qolib ketardi.
-
Bitta sinxron javob zimmasiga haddan tashqari ko‘p mas’uliyat yuklangan edi.Xabarni tahlil qilish natijasidan tortib biznes validatsiyasigacha (forma mavjudligi, guruh mavjudligi va forma tarqatilgan-tarqatilmagani) barchasi bitta javobning muvaffaqiyat/xato headeri orqali qaytarilgani uchun, javob xato qaysi bosqichda yoki nima sababdan yuz berganini ko‘rsatmasdi va qayta ishlash imkoniyati ham yo‘q edi.
Ushbu maqolada qabul qilish bosqichini "qabul qilinganda darhol saqlash va chiqish (Inbox pattern)" strukturasiga o‘zgartirish jarayoni, haqiqiy ro‘yxatdan o‘tkazish mantiqini esa Kafka hodisalariga asoslangan asinxron qayta ishlashga ajratish bayon qilinadi. Shuningdek, dizayn qarorlarining asoslari, jarayon davomida bekor qilingan qarorlar va amaldagi o‘lchovlar orqali tasdiqlangan natijalar ham qayd etiladi.
2. Texnologik tanlovlar asoslari — Muqobil variantlarni chiqarib tashlash jarayoni
2.1 Asinxron qayta ishlash usulini tanlash
"Avval javob berish, keyin qayta ishlash" yo‘nalishi tezda belgilandi, biroq uni amalga oshirishning bir nechta usuli mavjud edi. Har bir variantni chiqarib tashlash sabablari quyidagicha edi.
-
Ichki adapter thread pool yordamida asinxron qayta ishlash — Bu eng kam kod o‘zgarishini talab qiladi, ammo adapterda ma’lumotlar bazasi bo‘lmagani sababli, qabul qilingan elementlarni hech qayerda qayd eta olmaydi. Pod qayta ishga tushsa, qabul qilinganligi haqida javob yuborilgan, ammo hali qayta ishlanmagan elementlar izsiz yo‘qoladi. Eski tizim muvaffaqiyatli javob olgandan keyin elementlarni qayta yubormagani uchun, bu bevosita jo‘natmalarning yo‘qolishiga olib keladi.
-
Adapterga ma’lumotlar bazasini qo‘shish — Bu qabul yozuvlarini saqlab qolish imkonini beradi, ammo xizmatning "adapter ma’lumotlarga egalik qilmaydi" degan mohiyatiga zid keladi. Adapter ma’lumotlarga egalik qila boshlagach, manba xizmati bilan yangi sinxronlashtirish muammolari yuzaga keladi.
-
Asl xabarlarni to‘g‘ridan-to‘g‘ri message brokerga yuklash — Bu yo‘qotishlarning oldini oladi, ammo alohida elementlar holatini (qayta urinishlar soni, xato sababi va qayta ishlash yakunlangan-yakunlanmagani) so‘rash yoki operatorlarga ularni vizual tarzda tekshirish imkonini bermaydi. Katta ko‘lamli nosozlik vaqtida "Hozir nechta element yig‘ilib qolgan?" degan savolga javob bera olmaydigan struktura ekspluatatsiya qilish uchun qiyin bo‘ladi, degan xulosaga keldik.
Yakuniy tanlov Inbox pattern + event trigger bo‘ldi. Asl xabar allaqachon ma’lumotlar bazasiga egalik qiladigan survey xizmatida saqlanadi, survey xizmati esa uni faqat talqin qilinmagan asl xabar (opaque payload) va qayta ishlash metama’lumotlari sifatida ko‘radi. Xabar ma’nosini faqat adapter tushunadigan chegara saqlab qolinadi, saqlash va holatni boshqarish kabi infratuzilmaviy mas’uliyat esa ma’lumotlar bazasiga egalik qiluvchi xizmatga topshiriladi.
2.2 Qayta ishlash triggeri — Feign callback o‘rniga hodisalar
Saqlashdan keyin haqiqiy qayta ishlashni kim ishga tushirishi yana bir bahsli masala bo‘ldi. Agar survey xizmati to‘g‘ridan-to‘g‘ri adapterga (Feign) murojaat qilsa, bog‘lash oddiy bo‘lardi, biroq avval bir yo‘nalishli bo‘lgan adapter → survey bog‘liqligiga yangi teskari sinxron bog‘liqlik kiritilardi. Xizmatlararo aloqa matritsasi aylana shakliga kelgach, unga mos xatoni tarqatish yo‘llari ham paydo bo‘ladi.
Shu sababli, saqlash commit qilingandan so‘ng survey xizmati faqat domen hodisasini e’lon qiladi va adapter uni consumer sifatida qabul qilib, qayta ishlaydi. Natijada xizmatlararo kontraktga faqat bitta satr qo‘shildi: "adapter survey hodisalariga obuna bo‘ladi"; teskari sinxron chaqiruv esa kiritilmadi. Partition key sifatida survey forma raqamini ko‘rsatish qo‘shimcha xarajatsiz yana bir afzallikni berdi: bir xil nishonga tegishli hodisalar bitta partition ichida tartib bilan e’lon qilinishi va iste’mol qilinishini ta’minladi.
2.3 Qo‘llanilgan texnologiyalar steki
-
Apache Kafka — Saqlash yakunlanganini bildiruvchi hodisalarni e’lon qiladi va ularga obuna bo‘ladi. Partition key tartibni kafolatlash uchun infratuzilmaviy asosni ta’minlaydi.
-
PostgreSQL & JPA — Inbox persistence. Asl xabar JSON matni sifatida boshqariladi, holat, qayta urinishlar soni va xato sababi esa normallashtirilgan ustunlarda saqlanadi.
-
ShedLock — Fallback batch bir nechta instansiyada ortiqcha tarzda bajarilmasligi uchun taqsimlangan lock qo‘llanildi.
-
Redis — Tashqi tizimlardan ma’lumot qidirishni keshlash (§4.4) va jo‘natma metama’lumotlarini saqlash qatlami sifatida ishlatiladi.
-
Spring Cloud OpenFeign — Inbox ro‘yxatdan o‘tkazish, olish va holatni yangilash API’lariga murojaat qiladi. Kontrakt alohida client moduliga ajratildi va kompaniyaning ichki repository’si orqali tarqatildi.
3. Amalga oshirish jarayoni
3.1 Qabul qilish bosqichi — Nimani saqlash va nimani olib tashlash kerak
Migratsiyaning birinchi bosqichi qabul qilish vaqtida bajariladigan ishlarni minimallashtirishdan iborat bo‘ldi. Biroq biz barcha validatsiyalarni olib tashlamadik. Noto‘g‘ri formatlangan xabar saqlangan taqdirda ham tiklanmaydi, shuning uchun bunday xabarlar saqlanmaydi; buning o‘rniga darhol sinxron xato javobi qaytariladi. Qolgan barcha biznes validatsiyalari asinxron yo‘lga ko‘chirildi.
// 전환 전: 진입점이 등록 전체를 동기로 수행 (@Transactional은 DB가 없어 무효)
@Transactional
public void registerSurveySendInfo(SurveySendInfo info) {
// 레거시 조회 2회 + 계정 4~5회 + 설문 10여 회 ... 총 20여 회 REST 왕복
// 중간 실패 시 앞 단계 산출물(게스트, 수신자정보 등)이 롤백 없이 잔존
}
// 전환 후: 저장-후-종료
public void acceptSurveySendInfo(SrvySendInfoDto ipd) {
validateRequired(ipd); // srvyDrawupNo, patno, formCd 존재만 확인
smsMsgInfoAction.saveMsgInfo(ipd); // 발송 메타데이터 보존(Redis)
surveySendInfoInboxProxy.register( // 원문 그대로 저장 + 커밋 후 이벤트 발행
SurveySendInfoInboxCdo.from(ipd));
}
[Code 1] Qabul qilish entry point’ining o‘zgarishdan oldingi va keyingi holati. Eski tizim bilan kontrakt (URL, xabar formati va response header’lari) o‘zgartirilmadi.
Bu yerda biz aslida duch kelgan narsa majburiy maydonlar ro‘yxatini yakunlashDastlab jo‘natish sanasi va vaqtini ham majburiy deb hisobladik, biroq staging o‘lchovlari ishlab chiqarish muhitida jo‘natish sanasi va vaqti bo‘sh bo‘lgan xabarlar haqiqatan ham mavjudligini tasdiqladi. Mavjud mantiq tahlil muvaffaqiyatsiz bo‘lganda qabul qilish vaqtiga qaytib o‘tganligi sababli, buni validatsiyadan chiqarib tashlash, bo‘sh qiymatni qanday bo‘lsa shunday saqlash va qayta ishlash vaqtiga qaytib o‘tishni qoldirish orqali tuzatdik. Bu validatsiyani kuchaytirish odatda normal ishlayotgan trafikni deyarli bloklab qo‘ygan holat edi.
3.2 Holatlar mashinasi va yagona qayta urinish reyestri
Saqlangan yozuvlar beshta holat orqali o‘tadi: RECEIVED → PROCESSING → COMPLETED; qayta ishlash muvaffaqiyatsiz bo‘lsa, ular FAILED sifatida qayd etiladi, qayta urinishlar chegarasidan oshilgach esa DEAD holatiga o‘tadi va qo‘lda aralashuv talab qilinadi.
Loyihalashda biz eng ko‘p e’tibor qaratgan tamoyil yagona qayta urinish reyestrini yuritish ediXabar almashish qatlamidagi qayta urinishlar (sukut bo‘yicha 10 ta urinish, undan keyin DLT) ilova qayta urinishlari bilan ustma-ust kelganda, haqiqiy urinishlar sonini hech kim tushuntira olmaydi. Shu sababli consumer istisnolarni tashqariga chiqarmaydi; u hammasini ushlab qoladi va faqat Inbox'ga qayd etadi, qayta urinish haqidagi qarorlar esa faqat Inbox'ning attempt hisoblagichi va fallback batch orqali boshqariladi.
3.3 Atomik claim — takroriy qayta ishlashning oldini oluvchi yagona darvoza
Asinxron o‘tishdagi eng xavfli holat — takroriy hodisa iste’moli va batch orqali qayta nashr etish bir-biri bilan poygalashib, ayni yozuv ikki marta ro‘yxatdan o‘tkazilishidir. Buning oldini yagona shartli UPDATE operatori bilan oldik. Faqat o‘tish muvaffaqiyatli bajarilgan instance dastlabki xabarni oladi va qayta ishlashni davom ettiradi; qolganlari jimgina yakunlanadi.
@Modifying
@Query("UPDATE SurveySendInfoInboxJpo i " +
" SET i.status = 'PROCESSING', i.modifiedOn = :now " +
" WHERE i.id = :id AND i.status IN ('RECEIVED', 'FAILED')")
int claim(@Param("id") Long id, @Param("now") LocalDateTime now);
// 갱신 행 수가 1이면 점유 성공 → 원문 반환, 0이면 이미 점유/종결된 건 → null
public SurveySendInfoInboxRdo claimAndLoad(Long id) {
if (repository.claim(id, LocalDateTime.now()) == 0) {
return null;
}
return SurveySendInfoInboxRdo.from(repository.getById(id));
}
[Code 2] Claim o‘qishdan keyin yangilash emas, balki shartli yagona UPDATE bo‘lishi shart. O‘qish va yangilash ajratib qo‘yilsa, ular orasidagi interval poyga holatiga aylanadi.
Loyihalashning dastlabki bosqichida claim API va dastlabki xabarni olish API'sini ajratdik, biroq amalga oshirish vaqtida ularni muvaffaqiyatli claim bilan birga dastlabki xabar qaytariladigan tarzda birlashtirdikUlarni ikkita chaqiruvga ajratish nafaqat chaqiruvlar sonini oshirardi; balki claim muvaffaqiyatli bo‘lib, olish muvaffaqiyatsiz tugaydigan oraliq holatni ham yuzaga keltirardi.
3.4 Consumer — istisnolarni tashqariga chiqarmang
@KafkaListener(topics = SurveyTopic.SURVEY_SEND_INFO_RECEIVED, groupId = "...")
public void handle(SurveySendInfoReceivedDomainEvent event) {
SurveySendInfoInboxRdo rdo = inboxProxy.claim(event.getInboxId());
if (rdo == null) {
return; // 이미 점유·종결됐거나 선행 미종결 건이 있어 보류된 경우
}
try {
surveyFlow.registerSurveySendInfo(rdo.toSurveySendInfo());
inboxProxy.complete(rdo.getId());
} catch (PromsBizException e) { // 업무 예외 → 메시지를 그대로 사유로
handleFailure(rdo, e.getMessage());
} catch (Exception e) { // 시스템 예외 → 고정 문구
log.error("inbox 처리 실패. id={}", rdo.getId(), e);
handleFailure(rdo, "시스템 오류");
}
}
private void handleFailure(SurveySendInfoInboxRdo rdo, String reason) {
String status = InboxProxy.fail(rdo.getId(), reason); // FAILED 또는 DEAD 반환
if ("DEAD".equals(status)) {
amcFlowProxy.sendResult(rdo.getSrvyDrawupNo(), "N", reason); // 레거시 회신
}
}
[Code 3] Xatoni qayd etuvchi API DEAD holatiga o‘tish kerak yoki kerak emasligini belgilaydi va qaytaradi, adapter esa javob triggerini boshqaradi.
Qayta urinish siyosatida biz o‘rtasida farq qilmaslikka qaror qildik: biznes istisnolari va tizim istisnolari. Hatto ro‘yxatdan o‘tkazilmagan forma kabi biznes xatosi ham mas’ul shaxs formani ro‘yxatdan o‘tkazsa hal qilinishi mumkin, qayta urinish oralig‘i esa bir daqiqa bo‘lgani uchun xarajat deyarli ahamiyatsiz. Ularning farqi faqat legacy tizimiga qaytariladigan sabab xabarida namoyon bo‘ladi.
3.5 Fallback batch — to‘g‘ridan to‘g‘ri qayta ishlash o‘rniga faqat hodisani qayta nashr etish
Yo‘qolgan hodisalar yoki consumer uzilishlarining o‘rnini qoplash uchun batch qo‘shdik, biroq batch ro‘yxatdan o‘tkazish mantiqini to‘g‘ridan to‘g‘ri chaqirmasligiga ishonch hosil qildik. Faqat hodisani qayta nashr etish odatiy va tiklash yo‘llarini aynan bir xil kodga birlashtiradiBuning sababi shu edi. Shuningdek, tiklashga xos kod yo‘llari odatiy ishlash vaqtida bajarilmasligini hisobga oldik, shu bois ularga aynan kerak bo‘lgan paytda xulq-atvorini ishonchli deb hisoblash qiyin.
@Scheduled(cron = "0 * * * * *") // 1분 주기
@SchedulerLock(name = "surveySendInfoInboxRedeliver") // 다중 인스턴스 중복 방지
public void redeliver() {
// ① 이벤트 유실 의심: RECEIVED 이면서 생성 후 2분 경과
// ② 재시도 대상: FAILED 이면서 retryCount < 5
// ③ 처리중 고착: PROCESSING 이면서 15분간 갱신 없음 → RECEIVED 복귀 후 재발행
for (SurveySendInfoInbox target : InboxStore.findRedeliverTargets()) {
try {
eventPublisher.publish(SurveySendInfoReceivedDomainEvent.of(target));
} catch (Exception e) { // 한 건 실패가 라운드 전체를 막지 않도록
log.warn("재발행 실패. id={}", target.getId(), e);
}
}
}
[Code 4] Bitta batch tiqilib qolgan holatlarning uch turini qamrab oladi. Batch yozishda har bir yozuv uchun try-catch qo‘shishni odat sifatida qo‘llash xavfsizroq.
4. Muammolarni hal qilishdan olingan saboqlar
4.1 Qayta urinish xavfsizligi — himoya tekshiruvlarini sochib yuborish o‘rniga atomizatsiya qilish
Asinxron qayta urinishlarni joriy qilish uchun ro‘yxatdan o‘tkazish mantiqi bir necha marta ishga tushsa ham xavfsiz qolishi kerak. Bosqichma-bosqich ko‘rib chiqish natijasida takrorlanish xavflari aniqlandi, chunki qabul qiluvchi ma’lumotlari, rejalashtirish, tayinlash va vazifa ro‘yxatdan o‘tkazishning barchasi shartsiz ravishda yangi yozuvlar sifatida yaratilardi. Xususan, oldin ham takroriy rejalashtirishlar va tayinlashlar faol jo‘natish yozuvlarini vazifalarsiz qoldirib, keyingi batch'larning butunlay muvaffaqiyatsiz bo‘lishiga olib kelgan haqiqiy hodisalar yuz bergan edi.
Dastlab adapterga "mavjud yozuv bor-yo‘qligini orqaga qarab so‘rash" uchun API qo‘shish va har bir bosqichda himoya tekshiruvini biriktirishni ko‘rib chiqdik. Biroq bu yondashuv himoya tekshiruvlarini to‘rtta joyga sochib yuboradi va har safar yangi bosqich qo‘shilganda ayni xatoni takrorlash imkonini qoldiradi.
Buning o‘rniga survey xizmatiga to‘rtta bosqichni yagona tranzaksiyada birlashtiradigan bundle API yaratib, uni chaqirishni topshirdikBundle muvaffaqiyatsiz bo‘lsa, survey tomonida hech qanday ma’lumot qolmaydi va shu tariqa qayta urinishlar avtomatik ravishda xavfsiz bo‘ladi. Kirish nuqtasi allaqachon yakunlangan yozuvlarni qayta yuborish tarmog‘iga ajratib qo‘yganligi sababli, ushbu yagona atomizatsiya harakati adapter tomonidagi idempotentlik himoyalarini butunlay keraksiz qildi. Muhim jihat atomiklik uchun javobgarlikni ma’lumotlarga egalik qiluvchi xizmatga ko‘chirish edi.
4.2 Tartib kafolatlari va qolayotgan xavflar
Legacy tizimi batch va individual xabarlarni ajratmagani sababli, biz aniq qaror qilib kelib tushish tartibini biznes tartibi deb hisobladikPartition key buni infratuzilma darajasida qo‘llab-quvvatlaydi, biroq qayta urinishlar sababli ushbu faraz buzilishi mumkin bo‘lgan bir holat qolmoqda. Agar dastlabki jo‘natish xabari muvaffaqiyatsiz bo‘lib, qayta urinishni kutayotgan bo‘lsa, ayni subject uchun submission xabari avvalroq kelishi va tartibni teskarisiga aylantirishi mumkin.
Biz buni claim qilishdan oldingi nuqtada quyidagi himoya tekshiruvi bilan to‘sib qo‘ydik: ayni subject uchun avvalroq yakunlanmagan yozuv mavjud bo‘lsa, joriy yozuvni qayta ishlamasdan yakunlashUshlab turilgan yozuv batch tomonidan tartib bilan qayta nashr etiladi va oxir-oqibat kelib tushish tartibida qayta ishlanadi. Bu yagona indeks yordamida hal qilinadigan, xarajati past himoya edi va keyinchalik "ayni xabarni yana qabul qilishni qayta yuborish so‘rovi sifatida talqin qilish" siyosatini qo‘shganimizda ham xavfsizlik mexanizmi bo‘lib xizmat qilishda davom etdi.
4.3 Sinxron kontraktni qisqartirish qiymati — xabar berishdagi xatolar yo‘lini yaratish
Javob ma’nosi "ro‘yxatdan o‘tkazish yakunlandi"dan "qabul qilish yakunlandi"ga o‘zgargach, legacy tizimiga biznes validatsiyasi xatolari haqida xabar berish imkoniyati yo‘qoldi. Ushbu bo‘shliqni to‘ldirish uchun o‘sha paytda alohida loyihalanayotgan bir tomonlama natija xabarnomasi interfeysini kengaytirib, undan xatoga javob berish kanali sifatida foydalanish imkonini berdik.
-
Yakuniy xato (DEAD) yozuvlari — jo‘natishni yakunlab bo‘lmagani haqida, xato sababini ham qo‘shgan holda, qabul qiluvchiga xabar bering.
-
Jo‘natib yuborilgan xabar haqida bildirish — bu so‘rov faqat so‘rovnomani yaratishi va AlimTalk xabarini yubormasligi haqida sabab bilan birga xabar beramiz.
-
Allaqachon yuborilgan va qayta yuborib bo‘lmaydigan yozuvlar — qayta yuborish nima sababdan mumkin emasligi haqida xabar bering.
Ma’lumotlarga egalik qiluvchi survey xizmati qaror qabul qilib, uni javobda bildiradi, legacy xabar formatini tushunadigan adapter esa javobni o‘zi qayta ishlaydi. Biroq javob muvaffaqiyatsiz bo‘lganda uni yana yuborish vositasining yo‘qligi hamon muammo bo‘lib qolmoqda.
4.4 Tor joylarni taxmin qilmang — ularni o‘lchang — tashqi qidiruvlarni keshlash
O‘tishdan keyin ham o‘tkazuvchanlik kutilganidek oshmadi, shuning uchun monitoring vositasi yordamida bir nechta trace'ni o‘zaro tekshirdik. Natijada, har bir ro‘yxatdan o‘tkazish uchun hosil qilinadigan ikkita legacy qidiruvidan Rozilik shaklini izlash so‘rovlari odatda 760–984 millisekund davom etdiboshqa biri esa atigi 50–150 millisekund davom etdi. Kod tuzilmasi jihatidan bu ikki chaqiruv deyarli bir xil ko‘ringani uchun, instrumentatsiyasiz ular bir xil darajada sekin deb taxmin qilib, noto‘g‘ri qism ustida ishlashimiz mumkin edi.
Eski qismni yaxshilab bo‘lmasligini tasdiqlaganimizdan so‘ng, adapterda qisqa TTLga ega kesh yordamida yuklamani kamaytirdik. Biznes jarayonining bir qismi sifatida bir xil so‘rovnoma uchun bir nechta xabar bir vaqtda kelgani sababli, izlashlar umumiy soni "xabarlar soni"dan "takrorlanmaydigan so‘rovnomalar soni"gacha kamayadi.
public List<AmcSurveyConsent> findAmcSurveyConsent(String srvyNo) {
String key = "proms:amc:survey-consents:" + srvyNo;
try {
String cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return objectMapper.readValue(cached, new TypeReference<>() {});
}
} catch (Exception e) {
log.warn("캐시 조회 실패 — 원 호출로 폴백. srvyNo={}", srvyNo, e);
}
List<AmcSurveyConsent> result = amcProxy.findSurveyConsent(srvyNo);
// 동의서는 빈 목록도 캐시한다 — 동의서 없는 설문이 일반적이라 여기가 절감의 핵심
cachePut(key, result, Duration.ofMinutes(5));
return result;
}
[Code 5] Keshdagi nosozlik funksional xatoga aylanmasligini ta’minlash uchun kesh qatlamidagi istisnolar ogohlantirish sifatida jurnalga yoziladi, shundan so‘ng tizim dastlabki chaqiruvga qaytadi.
Ma’lumotlarning xususiyatiga qarab turli keshlash siyosatlarini qo‘lladik. Konfiguratsiya mavjud emasligini ko‘rsatuvchi natijalar keshlanmaydi. Agar mas’ul shaxs konfiguratsiyani tuzatganidan keyin ham eski natija besh daqiqagacha saqlanib qolsa, tuzatish nega kuchga kirmayotgani haqida murojaatlar kelib tushadi. Aksincha, bo‘sh rozilik shakllari ro‘yxatlari keshlanadi. Rozilik shakllarisiz so‘rovnomalar aslida odatiy holat bo‘lgani uchun, tejalishning katta qismi shu holat hisobiga to‘g‘ri keladi. Buning evaziga operatsiyalar jamoasiga eski konfiguratsiyalardagi o‘zgarishlar kuchga kirishi besh daqiqagacha vaqt olishi mumkinligini ma’lum qildik.
4.5 Istisnolar va tranzaksiya chegaralari — Nega noRollbackFordan foydalanmaslikka qaror qildik
"Jarayon muvaffaqiyatsiz tugashi mumkin, lekin xato qaydi saqlanib qolishi kerak" degan talab bo‘lsa, eng oson yechim istisnoni rollbackdan chiqarib tashlash uchun sozlashdir. Biroq bu yondashuv istisno turlari ko‘paygan sari ro‘yxatni yangilab borishni talab qiladi va barcha kodni o‘qib chiqmasdan qaysi holatlar saqlanib, qaysilari yo‘qolishini aniqlab bo‘lmaydi.
Shuning uchun, muammoni faqat tranzaksiya chegaralarini ajratish orqali hal qilish tamoyilini belgiladik. Band qilish, yakunlash va xato qaydini saqlashning har biri mustaqil REST chaqiruvlari va mustaqil tranzaksiyalar sifatida amalga oshiriladi. Asosiy jarayon muvaffaqiyatsiz tugasa ham, xato qaydi alohida tranzaksiyada bo‘ladi va shu sababli rollback qilinmaydi; natijada urinishlar soni va sabab saqlanib qoladi.
Hatto xatoni qayd etish ham muvaffaqiyatsiz tugaydigan eng yomon holatda rollbackka emas, vaqt o‘tishi bilan tiklanishga tayanamiz. Agar holat processing sifatida qolsa, batch job uni 15 daqiqadan so‘ng qaytaradi, band qilishning atomikligi esa takroriy ishlov berishning oldini olib, xavfsizlikni ta’minlaydi. Biroq bu yo‘lda urinishlar soni oshmasligini hujjatlarda aniq qayd etdik.
To‘liq tekshiruv jarayonida umumiy kutubxonada ham bo‘shliq borligini aniqladik. Ichki biznes-istisnolar klassi consumerning qayta urinishdan chiqarish ro‘yxatida yo‘q edi, ya’ni to‘g‘ridan-to‘g‘ri DLTga yuborilishi kerak bo‘lgan biznes istisnosi DLTga faqat 10 ta qayta urinishdan keyin yetib borardi. Inbox yo‘liga bu ta’sir qilmadi, chunki consumer istisno uloqtirmaydi, ammo bu muammo boshqa servislar consumerlariga bevosita ta’sir qilgani sababli, uni umumiy kutubxona uchun takomillashtirish bandi sifatida ro‘yxatdan o‘tkazdik.
4.6 Rollback usulini dizaynga kiritish
Joylashtirishdan keyin muammo yuzaga kelsa, rollback jarayonini oldindan tartibga solayotganimizda, bu o‘tishda an’anaviy image rollback ishlashiga to‘sqinlik qiladigan cheklov borligini aniqladik. Eski tizim acknowledgment javobini allaqachon olgani sababli xabarlarni qayta yubormaydi, eski versiyadagi adapterda esa consumer mavjud emas; natijada yakunlanmagan qolgan barcha elementlar doimiy ravishda ishlovsiz qoladi. Boshqacha aytganda, har qanday rollback yo‘lidan oldin barcha yakunlanmagan elementlar tugatilganini tasdiqlash kerak.
Shu sababli asosiy chora sifatida image rollback o‘rniga konfiguratsiya almashtirgichini tanladik. Faqat qabul qilish yo‘li eski sinxron usulga qaytarilib, consumer va batch job faol holatda qolsa, yangi kelganlar sinxron tarzda qayta ishlanadi, qolgan elementlar esa iste’mol qilinishda davom etadi va ularning tugatilishi tabiiy ravishda yakunlanadi.
// 롤백 토글: false면 Inbox를 거치지 않고 구 동기 경로로 즉시 처리한다.
// off 상태에서도 컨슈머·fallback은 유지되어 잔여 inbox 건은 계속 소화된다.
@Value("${proms.survey-send-info.inbox-enabled:true}")
private boolean inboxEnabled;
if (inboxEnabled) {
surveySendInfoInboxFlow.acceptSurveySendInfo(ipd); // 저장-후-종료
} else {
surveyFlow.registerSurveySendInfo(new SurveySendInfo(ipd)); // 구 동기 경로
}
[Code 6] Image’ni qayta joylashtirmasdan, faqat konfiguratsiyani o‘zgartirish va restart qilish orqali qabul qilish yo‘lini tiklash imkonini yaratdik.
Almashtirgichni loyihalashda deyarli e’tibordan chetda qoldirgan yana bir jihat ham bo‘ldi. Yuborish metama’lumotlarini izlashni Inboxga ustuvorlik beradigan qilib o‘zgartirgandik, ammo almashtirgich o‘chirilganda qayta qabul qilingan elementlar faqat Redisni yangilaydi va natijada javoblar eski Inbox qatorlaridagi qiymatlardan foydalanadigan teskari holat yuzaga keladi. Almashtirgich o‘chirilganda faqat Redis tekshirilishi uchun izlash nuqtasida ham xuddi shu almashtirgichdan foydalandik. Almashtirgich joriy etilganda unga murojaat qiladigan barcha nuqtalar birgalikda qayta ko‘rib chiqilishi kerakligini angladik.
5. Tekshiruv natijalari
Rivojlantirish muhitida real trafik bilan to‘rt bosqichli, staging muhitida esa bir bosqichli tekshiruv o‘tkazdik; ko‘rsatkichlarni jurnalga yozish va tracing vositalari yordamida bevosita o‘lchadik.
-
Qabul qilish javobi vaqti — Avvalgi 1,4–2,2 soniyadan median qiymat bo‘yicha 33 millisekundgacha kamaydi, ya’ni taxminan 50 baravar yaxshilandi. Eski tranzaksiyani band qilish muammosi tubdan hal qilindi.
-
To‘liq yakunlash — Barcha 111 ta xabar 38 soniyada, barcha 273 ta xabar esa 94 soniyada yakunlandi; xatolar ham, fallback aralashuvlari ham kuzatilmadi. O‘tkazuvchanlik taxminan sekundiga uchta xabarni tashkil etdi. Shuningdek, masshtablash zarur bo‘lsa, bo‘limlarga ajratish va consumer parallelligi foydalaniladigan asosiy vositalar ekanini tasdiqladik.
-
Tashqi izlashlar sonining kamayishi — Kesh joriy etilgach, eski tizimdagi izlashlar xabarlar soniga nisbatan taxminan 98% ga kamaydi (111 ta xabarni qayta ishlashda taxminan beshta cache-miss izlash).
-
Xato haqida bildirishnoma yo‘lini tekshirish — Real trafik orqali ro‘yxatdan o‘tkazilmagan shakllarga oid 29 ta holat beshta qayta urinishdan so‘ng DEAD holatiga o‘tganini va sabab bilan birga eski tizimga qaytarilganini tekshirdik.
-
Avtomatik xatoni tiklashni tekshirish — Tasodifan staging muhitidagi tekshiruv eski tizim ma’lumotlar bazasidagi qisqa uzilish bilan bir vaqtga to‘g‘ri keldi. Barcha 273 ta xabar dastlab muvaffaqiyatli qabul qilindi, ammo birinchi bosqichda har bir xabar muvaffaqiyatsiz tugadi. Ma’lumotlar bazasi tiklangach, batch orqali qayta nashr qilish natijasida barcha 273 ta xabar 3,5 daqiqa ichida avtomatik tiklandi (yakuniy xatolar soni nol). Ko‘zda tutilgan dizayn faqat rejalashtirilgan ssenariyda emas, balki haqiqiy nosozlik vaqtida ham ishlagani sababli, bu o‘tishdagi shaxsan men uchun eng mazmunli natija bo‘ldi.
6. Retrospektiva — Kelgusida qo‘llash kerak bo‘lgan saboqlar
-
Javobning ma’nosi o‘zgarganda, uning ta’sirini boshidan oxirigacha kuzatib chiqishimiz kerak. "Ro‘yxatdan o‘tkazish yakunlandi"dan "Qabul qilish yakunlandi"ga o‘tish kodning atigi bir necha qatorini qamrab oldi, ammo ikkita asosiy talabni yuzaga keltirdi: xato haqida bildirishnoma yo‘lini yaratish va rollbackdan oldin tugatish jarayoni bajarilishini ta’minlash. Men shartnoma matniga emas, avvalo uning ma’nosi o‘zgargan-o‘zgarmaganiga e’tibor berishni odat qildim.
-
Idempotentlikka ko‘proq himoya tekshiruvlarini qo‘shishdan ko‘ra, chegaralarni ko‘chirish orqali yaxshiroq erishiladi.To‘rtta joyga himoya tekshiruvlarini biriktirish o‘rniga, operatsiyani ma’lumotlarga egalik qiluvchi servisda atomik operatsiyaga ajratdik. Natijada barcha himoya tekshiruvlari yo‘qoldi va yangi bosqichlar qo‘shilganda ham tizim xavfsizligicha qoldi.
-
Ishonchli bo‘lishi uchun tiklash yo‘li odatiy yo‘l ishlatadigan ayni kodga kelib tutashishi kerak.Batch bevosita qayta ishlash o‘rniga faqat hodisalarni qayta nashr etishi kerak, deb qaror qilganimiz sababli, tiklash yo‘li haqiqiy hodisa paytida birinchi marta ishga tushirilganda ham qo‘shimcha tekshiruvlarsiz ishladi.
-
Tor joylarni faqat ular uchun metrikalar yig‘ish yo‘lga qo‘yilgandan keyingina bartaraf etish kerak.Treyc loglariga qaramaganimizda, shakli bir xil ko‘ringan ikkita so‘rovdan biri ikkinchisidan 15 marta sekinroq ekanini bilmagan bo‘lardik.
-
Orqaga qaytarish tartiblarini dizaynga kiritish dizayndagi kamchiliklarni ochib beradi.Orqaga qaytarish tartibini yozish jarayonida quyidagi cheklovni aniqladik: "tasdiq javobi allaqachon yuborilgan holatlarni eski versiyada qayta ishlashning hech qanday imkoni yo‘q edi." Bu bizga birinchi navbatdagi yaxshiroq chorani — toggle’ni — tayyorlash imkonini berdi.
Hali bajarilmagan ayrim vazifalar mavjud. Javob muvaffaqiyatsiz bo‘lganda uni qayta yuborishning imkoni yo‘q, shuningdek, dastlabki matnda shaxsiy ma’lumotlar ochiq ko‘rinishda bo‘lgani sababli yopilgan holatlar uchun saqlash muddatini va tozalash batchlari oralig‘ini yakuniy belgilashimiz kerak. Bu ikkala masalani keyingi ishlar sifatida boshqaryapmiz.
conley