The Jerusalem PostJerusalem Orchestra East & West youth division to perform at UNוואלה164 קמ"ש במקום 60: נהגת חדשה בת 18 נתפסה במהירות קיצונית בלב חיפהRTP DesportoPresidente do V. Guimarães agastado com início de época "frustrante"Daily MaverickStudent opens fire outside school in Turkey, eight pupils wounded, NTV reportsThe South AfricanBombshell: Springboks may play Malcolm Marx in new positionStraits Times SportLa Liga president hits out at Real Madrid refereeing ‘conspiracy’ claimsХабрИИ режет не рутину, а расходы. Поэтому подушка теперь базовый минимумOnetPolski "Niedźwiedź" pokazał się światu. Żołnierze powinni być zachwyceniIl Fatto QuotidianoMorto Grigory Ponomarev: l’addio improvviso a 31 anni di “Grizzly”, ex lottatore MMA. L’ipotesi di un legame con l’infortunio del 2023CNN بالعربيةأستراليا تؤكد تحويل أجزاء من مقاتلات F-35 الأمريكية إلى هونغ كونغ بالخطأSportstarLIVE India vs Sri Lanka, Asian Games 2026 hockey: IND 10 - 0 SL, Abhishek, Jugraj score two eachVilaWebLa UE tanca la discussió sobre l’oficialitat del català en set minuts i sense adoptar cap decisió
The Daily Newsstand · Free, Always
Tuesday, September 22, 2026

История миграции CDRs в чарджинг системах

Translate

Около 5 лет назад я участвовал в миграции OCS с Oracle на Apache Ignite и обновлении стека самого чарджинга. Это была огромная по масштабам задача, так как она существенно меняла не только основную базу данных, но и систему доставки CDRs и модернизировала стек. Задача была выполнена. Хочу рассказать об одной интересной проблеме, которая связана с NATS — его решили использовать как очередь для экспорта CDRs в Hadoop.

В старой версии OCS Oracle выполнял роль временного хранилища CDRs, и особых проблем не было — связка работала годами. Так как от Oracle отказались, было решено модернизировать доставку CDRs и использовать NATS. NATS согласно документации был современным и дешёвым решением, а документация обещала отсутствие серьёзных проблем. В реальности же NATS никто не тестировал — просто посмотрели документацию, сделали небольшой стресс‑тест и решили: идём в NATS, тем более там есть поддержка кластера и, как казалось тогда, это должно работать без проблем. Сказано — сделано.

Экспорт CDRs

Экспорт CDRs

В подобной архитектуре ничего не предвещало беды, но требования были следующими

  1. CDRs должны поступать в Hadoop согласно времени генерации

  2. NATS не должен перемешивать CDRs

  3. CDRs не должны дублироваться

  4. В случае краха мастера всё должно переезжать автоматически или с минимальными усилиями со стороны поддержки.

  5. NATS должен хранить события около суток (использовалась персистентность)

При анализе требований всё выглядело нормально: была сильная вера в то, что NATS справится. Не буду драматизировать, но все тесты включая стресс‑тест — 7 дней под 80% нагрузкой, — система прошла успешно. Если бы всё было хорошо, статьи про это не было бы.

Первый продакшн

Решение было поставлено в продакшн, проработало несколько месяцев — и начались первые сюрпризы:

  • Выяснилось, что по какой‑то причине NATS начал дублировать события, и экспортеры стали получать дубликаты. Поначалу казалось, что что‑то не так, но проблема с дубликатами нарастала.

  • Обнаружилось, что иногда при невыясненных обстоятельствах события застревали в очередях и случалось переупорядочивание. Причины мы так и не смогли выяснить, думаю, что проблема не решена до сих пор.

  • Иногда по какой‑то причине NATS отклонял входящие события. Иногда это случалось из‑за того, что кластер разрушался под нагрузкой, а иногда без видимых причин.

Проводились разные эксперименты с разными топиками, очередями и прочим. Но ни одно решение не давало стабильный результат, и тесты в лабе порой проходили по 2–3 недели, чтобы понять — а работает ли NATS или нет.

Наши недооценки

Мы напрасно заложились на то, что NATS будет неким Silver Bullet и всё будет так, как мы запланировали. В результате наших исследований выяснено:

  • NATS гарантирует естественный порядок событий в очереди: кто первый пришёл, тот первый вышел. Но иногда из‑за конкуренции экспортеров под трафиком возникают дублирования.

  • NATS не способен сортировать события по таймстемпу, который лежит внутри события. Он его просто игнорирует и сортирует строго по времени поступления.

  • Сериализация в JSON оказалась достаточно затратной и под нагрузкой вносила серьёзный overhead на обработку событий самого OCS, которые более важны, чем CDRs.

Как мы решили эту проблему и что из этого вышло

  • Было решено писать обёртку, которая задерживала события от экспортера и сама выполняла переупорядочивание.

  • Обёртка работала с Hadoop и сама проверяла, не задублировал ли что‑то NATS.

  • Обёртка стала неподдерживаемой — любое вмешательство или неосторожность могла разрушить всё.

  • Обёртка не могла гибко скейлиться, так как она не могла общаться с соседним экспортером. Решения из серии «загрузить в БД или куда‑то ещё» не прошли валидацию из‑за того, что решение получалось громоздким и потребляло много ресурсов.

  • В итоге мы потеряли scaling для экспортера

Итоги

На данный момент я знаю, что проблема с NATS и экспортом CDRs не решена. Обёртка превратилась в некого монстра, но продолжает работать, и в ней находят новые проблемы. На переработку решения времени не выделяют. Тестирование превратилось в квест.

Как бы я решал это сейчас

Если бы эта проблема решалась сейчас, я бы предложил собственное решение — HurriCache. HurriCache позволяет решать подобные задачи. Из набора контейнеров HurriCache я бы предложил использовать OrderedMap, в качестве веса — таймстемп из CDRs. Экспортеры вычитывали бы данные, используя атомарную операцию streamElementInRangeOrderedMap по таймстемпу. Импортеры использовали бы вставку через addElementOrderedMap, используя таймстемп в качестве веса. В HurriCache вес — это uint64.

Архитектура бы выглядела следующим образом

Возможная архитектура

Возможная архитектура

Как это решило бы старые проблемы

  • Проблема дубликатов. На схеме экспортеры используют операцию getAndRemoveElementWithWeight. Это атомарная операция: элемент читается и удаляется из OrderedMap за один шаг. Если экспортер упадёт сразу после чтения, но до удаления (как это было бы при неатомарной операции), при рестарте он забрал бы тот же CDR — или следующий, который уже мог забрать его коллега. В старой схеме экспортер был один из‑за сложной обёртки, здесь мы получили бы scaling из коробки.

  • Проблемы падения. Если экспортер упал во время операции — это не решено ни в прошлой схеме, ни в текущей.

  • Проблема переупорядочивания. В NATS порядок зависел от времени поступления в брокер. В HurriCache (через OrderedMap) порядок определяется весом — таймстемпом самого CDR (addElementWithWeight). OCS кладёт данные с весом, равным времени генерации события. Экспортер вычитывает диапазон и получает данные строго отсортировенными по времени генерации, а не по времени попадания в очередь. Даже если OCS worker положил данные позже, они всё равно будут вычитаны экспортером, так как запрос всегда идёт с гэпом, чтобы подобрать все события из прошлого.

  • Проблема конкуренции. Несколько OCS worker node могут писать в один кластер HurriCache параллельно — операции добавления масштабируются. Несколько экспортеров могут читать из него параллельно, так как они работают с непересекающимися диапазонами весов (временными окнами) или используют атомарное удаление, исключающее гонку за одним элементом.

  • Нагрузка на OCS. Убирается необходимость сериализации в JSON для передачи в брокер сообщений. HurriCache отлично работает с бинарными данными, что позволяет уменьшить стоимость сериализации и снизить CPU overhead на стороне OCS.

  • Отказоустойчивость. HurriCache представлен как кластер. Если один узел падает, данные остаются доступными. Если падает экспортер — данные не теряются и не дублируются, а ждут следующего цикла обработки. В данном случае отказоустойчивость не изменилась, так как NATS тоже работает в кластере и к этой функции претензий не было.

Вместо заключения

Данный опыт научил тому, что не всегда стоит доверять документации. Документация — это хорошо, но нет ничего лучше, чем качественно проведённые нагрузочные тесты, и не стоит ими пренебрегать, даже в ущерб времени. Оно точно окупится, но позднее.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.