Дотепність і відвага: як ми багато разів помилялися, створюючи iFunny

Це — не стаття, це — фейлбук. Те, що ви прочитаєте під катом, — витримка наших безглуздих техно-промахів за всі 5 років роботи над флагманським продуктом — iFunny. Можливо, наша фейловая історія допоможе вам уникнути помилок, а можливо, викличе сміх. Що теж добре. Смішити людей — покликання FunCorp вже 13 років.

Кілька ввідних про команду і продукті

Кістяк команди ми сформували ще студентами в далекому 2004 році в Пензі. Багато років робили щось мобільний і, як правило, смішне. У 2010 році, у низці багатьох, ми випускаємо АйДаПрикол — iOS-додаток для перегляду веселих картинок. Без особливих вкладень воно заходить на аудиторію Росії і СНД.
«Why not?» — думаємо ми і вже в 2011 робимо версію для північноамериканського ринку. За перші 12 місяців iFunny з App Store і Google Play скачають більше 3 мільйонів людей, а за 5 років продукт перетвориться на повноцінний розважальний соцмедіа вельми популярне в США (DAU = 3,5 M).
Основною частиною аудиторії залишаються користувачі iOS — і Android-додатків. Весь функціонал доступний саме там. Але Web-версія поступово наздоганяє app'и як з аудиторією, так і за наповненням. Щодня юзери постять в iFunny близько 400К мемів, а переглядають більше 600M.
Часто доводиться чути: “Та що тут складного, звичайна листалка картинок. Невже після роботи цим не можна займатися?" Однак крім реалізації хитрою бізнес-логіки (у нашому випадку entertainment-логіки), це ще й серйозний технічний челлендж під капотом. Ось, наприклад, з чим доводиться справлятися:
  • 15 000 RPS піку приходять бекенд-серверів, які формують відповідь за 60 мілісекунд;
  • 3,5 мільярда подій в день пишеться і аналізується в Data Warehouse;
  • 5,5 петабайт контенту в місяць лунає з допомогою CDN. У піку це 28 000 запитів в секунду, або 44 Гбіт в секунду.
В самому першому варіанті ця стаття була на 22 сторінки: зібрали в неї все, що тільки згадали. Потім вирішили, що це буде ще один фейл, тільки тепер на Хабре, і вибрали саме характерне: про плутанину з сховищами, помилки в підрахунках DAU, БДСМ-ігрища з Mongo, специфіку аудиторії. І невеличка інтрига: ми зводили Трампа і Путіна разом, коли це ще не було мейнстрімом.
Highload-проект
До iFunny у нас було багато проектів, але жодного з приставкою highload. Самою вражаючою вважалася щоденна відвідуваність в 100 000 унікальних відвідувачів. Тут все відбулося занадто різко. Ми не справлялися з навантаженням та обсягами даних, тому регулярно міняли то хостерів, то технології.
Виглядало це приблизно ось так:

Кожен переїзд був викликаний своїми причинами: де-то було дорого, десь не вистачало функціоналу, а десь, як у випадку з Hetzner, змушував тікати сам хостинг. Ми були досить небезпечним сусідом, якого регулярно ддосят і закидають обґрунтованими скаргами на заливаються користувачами сиськи/піськи та іншу розчленівку.
Як ви встигли помітити, особливо у нас виходило бігати від заліза до хмар і назад. Про випадок з «назад» як раз і розповімо.

Втеча з перешкодами

На початок 2012 року програма та база даних перебували на AWS, а контент незабаром почнемо роздавати через CDN, пройшовши тернистий шлях від одного 10гбіт сервера до декількох з rsync.
Але вже до травня наша вертикально-масштабована MySQL на AWS упирається в стелю і починає давати збої під навантаженням. А оскільки це RDS, то ручок ми майже не можемо покрутити. Розуміючи, що хмари — це все ж про горизонтальне масштабування, а нам влом, вирішуємо переїхати на OVH, який виправдав себе ще при роздачі контенту.
Будь-який нормальний розробник почав би думати про зміну архітектури, шардировании, але ми в черговий раз пішли по шляху найменшого опору, тобто вертикального масштабування. Першим ділом забрали MySQL в OVH. Продуктивність зросла, тому що це все-таки залізо, а не віртуалізація, у якої є невеликий оверхэд і купа нюансів з дисковим I/O. При цьому залізо взяли потужний 8 — ми ядерний Xeon, 24 ГБ оперативки і 300 ГБ SSD, а по грошам вийшло дешевше AWS.
Пізніше розкрився цікавий факт. Коли копалися в логах, помітили російська IP, який крав контент, посилаючи 50 запитів в секунду. Це створювало велику навантаження на API-бэкенды, особливо на MySQL. Саме ці запити стали однією з причин стрімкого відпливу на OVH. Після того як ми все це справа відфільтрували, базі даних стало ще легше, тільки вже в три рази.
Засмутилися тоді не сильно, тому що 50 реквестов в секунду могли б і за пару тижнів набігти з новими користувачами. Але вирішили помститися, тому що викачування йшла вже дуже жорсткими методами і великими сторінками. Віддали в відповідь замість картинок 700-мегабайтний дистрибутив Убунты, замаскований під jpeg. Не шкодуємо — були дуже злі. Це зараз при пікових 15000 RPS можна тільки посміятися над «проблемними» 50, але тоді це було істотно.
У підсумку раділи потужностей OVH недовго. Через рік після переїзду у них виникли проблеми з оперативним наданням нових серверів. Затримка доходила до місяця і це просто велінням їх засновника. Ми ж продовжували рости і від такої політики серйозно постраждали. Повернулися всім складом на AWS, де вже є перевірені рішення, SaaS продукти, а головне — гнучкість. Хай там суворий enterprise робить capacity planning, а ми підемо писати код. І тепер все більше розуміємо, що якби не хмари і SSD на перших порах — нас би взагалі не існувало. Як і цієї статті.
Зараз намагаємося по максимуму використовувати managed-сервіси AWS: S3: Redshift, ELB, Route 53, CloudFront, ElasticMapReduce, Elastic Beanstalk. Для невеликої команди це вихід, працювати ефективніше. Але це зовсім не означає, що можна з допомогою чарівної технології вирішити проблему. Той же: Redshift ми вже глибоко копнули: довелося грунтовно вивчати потрухи. Тому все це, як і NoSQL, точно не срібна куля. Потрібно розбиратися в дрібницях.
Швидке зростання

ЛжеDAU

Все той же 2012 рік. У період смути і хитання в технологіях у нас все добре з зростанням: ТОР-3 в App Store і зліт денний відвідуваності.
Але як відомо, будь-яка смута породжує парочку Лжедмитриев. Ми зіткнулися з двома випадками ЛжеDAU.
Перший стрибок.
Несподівано за 2 дні отримуємо підозрілі +300 тисяч: 2200K перетворилися в 2500K.
Причину з'ясували швидко. iFunny можна користуватися анонімно, не реєструючись. Такі юзери теж враховуються. Цим і скористався хтось, расковырявший наше API. Просто надсилав запити, підставляючи рандомные ідентифікатори, які ми і вирішили.
Другий стрибок.

Як тільки ми перенесли базу на OVH і вона перестала гальмувати, у нас підскочив DAU. Ми на радощах подумали, що UX — це вам не жарти. Програма стала працювати швидше і всі відразу почали нею користуватися. Тому ми з ще більшим ентузіазмом стали готуватися до первоапрельскому корпоративу — рекорд же. Але на жаль, це був не рекорд, а період розтягнувся на кілька тижнів переїзду програми з AWS на OVH. Там воно повинно було зустрітися з базою даних і зажити разом з нею довго і щасливо. Ця нестиковка виявила некоректний підрахунок DAU в MySQL. Розумні хлопці вважали б за access-логів, а навіщо ми робили неатомарные запити виду «якщо в цей день юзер ще не заходив, то зроби +1 до лічильника» на кожну дію користувача. У результаті чим довше тривали ці запити до бази, тим більше ставало DAU з-за паралельних запитів. :facepalm:
Перенесли реєстрацію і підрахунок DAU у Redis у звичайний Sorted Set — це вирішило проблему. Нам сподобався такий патерн, але за три роки ми перетворили код в локшину з підрахунку метрик в Redis. У 2016 запровадили збір серверних і клієнтських подій допомогою fluentd + S3 +: Redshift. Це позбавило нас від спагетті в коді. Про те, як це відбувалося, треба писати окремий пост. І навіть не один.

Redis-Пане

згодом став підбивати Redis, з яким раніше проблем не було. Пік проблем припадав на ніч. Він просто ставав недоступний PHP, і все відразу лягав, оскільки майже на кожен HTTP реквест нам треба було ходити до нього.
Починаємо розбиратися: дивимося на Redis-процес, расчехляем strace, а там «Too many open files». Виявляється, у нас ліміт 1024 з'єднання, на який не впливає параметр maxclients. Запускаємо з ulimit -n 60000 — максимальна кількість з'єднань піднімається тільки до 10240 і не більше. Тут розкопали дивовижну річ: в Redis 2.4 і старше стояв hard limit на 10240 сполук. Через пару днів поставили Redis версії 2.6 RC5, включили persistent з'єднання до нього і зітхнули з полегшенням, знаючи, що тепер-то нам точно вистачить дескрипторів.

Mongo-БДСМ

Командний зростання у нас почнеться тільки в 2013-14 роках. Крім QA, сформується команда DevOps, укомплектуется бекенд і фронтенд. В розробці мобільного по троє девелоперів на iOS і Android. Але на самих ранніх етапах так і залишалося півтори людини. Звідси брак ресурсів, недосипання з-за різниці в часі та банальна неуважність. А доводилося робити багато чого. Наприклад, кілька разів переробляти API або повністю переписувати код на бэкенде під роботу з MongoDB. Хоча багато її за нормальну базу даних і не вважають.

Хто де черпає натхнення, а ми у YouPorn. На відомому у вузьких колах Highscalability вичитали, що в якості основного сховища даних вони використовують Redis. Звичайно, страшно тримати все в оперативній пам'яті, потрібні непрості архітектурні рішення. Але якщо YouPorn сидить аж на Redis, ми хіба з MongoDB не впораємося?
Так, іноді з нею було приємно, але частіше вона робила атата батогом і вставляла кляп. Цей роман триває донині, але тоді ми, у статусі першого хлопця, випробовували багато істерики і косяки на собі.
Виправданням всьому був шардінг з коробки і, звичайно, хайп. В нашому випадку це було зручно, тому що ми ліниві і не хотіли обтяжувати себе написанням додаткового коду і займатися питаннями балансування даних. Ще в MongoDB нас залучили in-place updates, schema-less & embedded documents. Вона змусила нас замислитися про підхід до проектування структури даних в високонавантажених сервісах. Так почалося переписування і міграція з MySQL. Це приблизно середина 2012-го і ми почали користуватися MongoDB 2.0.
Поганий знак, що передбачив всі наші майбутні поневіряння, був уже на самому початку. Помилку допустили в процесі переписування. Справа в тому, що будь-мем, який заливається в iFunny, може бути забанений: для цього необхідна певна кількість скарг. Ми виставили порогові значення MongoDB, а коли запустили в експлуатацію, вони продовжували читатись з MySQL. Там вони були майже нульові і буквально одного-двох скарг вистачало, щоб забанити роботу. А з урахуванням наших тролів… 4000 добірних мемів зникли з Featured. Починилось все за день, але склалася ситуація повна драматизму за нашою банальної неуважності.

Міграція

Через 5 місяців переписування коду, у вільний від гасіння пожеж час настав день великої міграції, яка відразу провалилася: MongoDB не витримала навантаження. Тому що забули багато індексів накинути. Тому що купу багів створили. Погіршував становище сирої MongoDB-PHP драйвер з витоками пам'яті і проблемами з «форкнутыми» скриптами. Хто тут сказав про тестування? Не було ніякого тестування на той момент, нам же колись — ми пиляємо нові фічі і відразу в продакшн.
В той день наші користувачі в надії посміятися хоч десь нахлинули на ресурси 9GAG і благополучно його поклали. Хоч одна радість.
Різниця в часі теж підкидала проблем: коли в США найбільша активність — у нас ніч. І ось як ми виглядали на наступний ранок, коли треба було лягати спати:

Два тижні кропіткої праці і ми зважилися на другу спробу. Все пройшло гладко, майже ідеально. З'явилося розуміння, що без реплік і шард далеко не поїдемо, а з MongoDB це було легко провернути.
Все було добре, поки не вийшли на пікову навантаження. База не витримувала нічний наплив користувачів, і ми знову прилягали. При цьому були незрозумілі причини гальм — CPU або IO, прилади ні про що толком не говорили. Після чергових безсонних ночей, з допомогою доброго самаритянина, знаходиться джерело проблем: стандартний аллокатор пам'яті в MongoDB погано працює під нашою навантаженням. В результаті підмінили аллокатор на tcmalloc і стали чекати ночі. Вуаля, навантаження впала в 3 рази і ми стали іноді спати по ночах. Тоді зрозуміли ще дещо важливе: бази даних теж пишуть люди. В наступній версії розробники MongoDB 2.6 стали включати tcmalloc в поставку за замовчуванням.
Весь цей час наш саппорт розгрібав купу листів зі скаргами на роботу додатка:

Мабуть, тоді у нас склалася унікальна ситуація. В черговий раз підтверджувалося, що з такою високою навантаженням мало хто стикається. Або перефразовуючи інакше: ми єдині баламути, хто на такому навантаженні використовують подібні технології і в принципі припускає, що вона може йти на одну ноду.
Якщо повернутися в минуле і знову вирішувати, мігрувати на MongoDB, то відповідь була б однозначна: як primary storage — не треба. Зараз робота над помилками виглядала б так: для початку позбутися джойнов, зробити шардінг з віртуальними шардами, унести лічильники куди-небудь і вважати їх асинхронно, а для schema-less використовувати щось на зразок jsonb з PostgreSQL. Хто знає, може мігруючи знову. Ми не зарікаємося.
Аудиторія
У аудиторії гумористичного додатку є один величезний плюс — можна посміятися над усім. Наприклад, над тим, як у нас падають сервера:

Також ніхто не заважає влаштувати голосування на звання «Best on toilet app»:

Часто нам падають листи з подяками за те, що додаток допомагає впоратися з депресією. Гумор лікує:

Все це прямо впливає на нас.
Перший час після запуску iFunny технічного браку було так багато, що юзери прозвали команду розробки мавпами. Ми не заперечуємо. Погляньте хоча б на логотип FunCorp.

Але є й інша сторона.
По-перше, користувачів дуже багато. Найменша помилка — і саппорт перевантажений.
По-друге, їм хочеться самостверджуватися. У порядку речей влаштувати рейд на контент і мінусувати все підряд.

У вас тут API не працює

Перестали працювати наші API-методи. Посипалися скарги. Гіпотез багато, думали навіть про блокування стільниковими мережами. Але коли набралася достатня кількість спеціальних скарг, ми змогли знайти закономірність. Це були айфони з jailbreak. Тоді було модно знімати захист і впроваджувати свої твіки з власними шрифтами та іншими рюшечками. Багатьох, особливо підлітків, це бавило і допомагало виділитися. На один з таких твіков у нас трапилася алергія: він перехоплював вихідні HTTP-запити і видаляв деякі заголовки з них перед відправкою на сервер. Стали попереджати користувачів про проблеми з конкретними твіками, на цьому і розійшлися.
Про такі нюанси ми навіть не думали, але більша аудиторія робить свою справу. Зараз не згадаємо скільки таких було, 10 або 50 тисяч. Щодо всього додатки — це мізер, але для саппорта… ви вже бачили як це виглядає.
Висновок: репутація страждає, навіть якщо криворукий не ти. За весь довгий досвід розробки ми натикалися на баги і крэши у всіх, починаючи з Flurry і AWS, закінчуючи Apple і Google. Це було як по нашій репутації перед користувачами, так і з DAU.
Неуважність
Наостанок про неуважність. Через людського фактора трапляється багато помилок. І ми не виняток.

146% може організувати не тільки Чуров

У iFunny з'явився веб-апп для голосувань. Так зійшлися зірки, що за браком часу у бекэндеров і за маєтком бажання у фронтендеров написати бекэндный код, голосування вийшло неатомарным. Оновлення кількості голосів йшло через звичайний інкремент поля в коді, а для бази це типовий read-before-write без локов. Вийшло розбіжність в кращих традиціях російських виборів. Користувачі, звичайно, нас потроллілі. Можливо, вони навіть подумали, що це було зроблено спеціально. Але тільки на Хабре ми розповімо всю правду.
Свідоцтво ганьби не збереглося, але збереглися промовисті коментарі:

У наступному голосуванні все вже було правильно, але якщо ще раз порахувати в нашому стилі, то можна прийти до висновку, що Трамп, який отримав 48% голосів, програв би Путіну майже в 2,5 рази.

Банальне нехлюйство

Збираєш, збираєш користувачів, вовлекаешь їх, веселишь нещадно, а потім забуваєш про якусь дрібницю, і все. Немає їх. Собственноручный відстріл аудиторії ми влаштували 2 роки тому.
Зробили підтримку бейджиків для Android-додатки. У 2015 вони з'явилися тільки у Samsung. Нотифікація була класна і не дратує. Через деякий час бейджики зламалися і ніхто цього не помітив. Ще через кілька місяців це помітив один з продактов. Ми все полагодили і додали в DAU 120К.
Виявляється, уважніше треба бути всім: і QA, і продактам, і розробникам. І тобі. Так-так — тобі. От читаєш зараз цей лонгрид, а тим часом десь щось ламається. Не будь як ми, піди перевір.
А ми відновлюємо блозі на Хабре, де будемо ділитися новими-старими історіями.
У 2013 нас вистачило тільки на один пост про оптимізацію анімації To Gif or not to Gif. За минулі роки накопичилося багато цікавого, про що хотілося б розповісти: AWS, NoSQL, iOS, Android, математика в стрічках, Big Data, премодерації контенту, спам. Іноді це навіть не фейли, а вдалі рішення і на кожне знайдеться ціла гора мемів. Тому більше гумору в життя, адже Fun Happens :)
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.