Як зробити кеш браузера не таким корисним, як зазвичай

Хочу розповісти вам про те, як ми в Яндекс.Браузері спробували зробити кеш не таким марним для користувачів, як зазвичай. У нещодавно вийшла нової бете Яндекс.Браузера для Android (плануємо і для інших ОС) можна отримати доступ до нещодавно відвіданих сайтів навіть при відсутності з'єднання з інтернетом. Причому це повинно працювати набагато надійніше і зручніше, ніж все, що ви бачили до цього.



Щоб це стало можливим, ми придумали власний кластерний кешування, алгоритм роботи якого стежить за тим, щоб зберігати сторінки максимально цілісно. Подробиці про пристрій всього — під катом.

Постійний і безперебійний доступ в інтернет в будь-якій точці світу або хоча б міста, як і раніше залишається мрією. Тому в реальності ми так чи інакше стикаємося з офлайном і неприємностями, які він породжує. Наприклад, ви не зможете продовжити читання раніше відкритого поста в браузері, якщо оновіть вкладку при відсутності WiFi/3G/LTE. Або вона сама вирішить оновитися, тому що була вивантажена з оперативної пам'яті. Або ви вирішите повернутися на попередню сторінку за допомогою кнопки «Назад». Ситуації бувають різні, але кожна з них буде помилкою, тому що браузер не зможе завантажити матеріал з мережі. Стоп! Але адже він вже був завантажений на ваш пристрій. Все, що ви шукайте, вже лежить в браузерном кеші. Так чому не можна це використовувати? Звучить досить просто, правда?

Саме це наша команда і взялася реалізувати в Яндекс.Браузері. Ми поставили перед собою завдання домогтися передбачуваною і якісної роботи в офлайні з використанням браузерного кеша.

Кешування звичайне

Якщо звернутися до досвіду десктопних браузерів, то можна згадати автономний режим Internet Explorer або Firefox, який полягав у спробах браузера завантажити потрібну сторінку з збереженої копії або кешу. Десять чи навіть двадцять років тому це було особливо актуально з-за широкого розповсюдження dialup та оплати за час, проведений в мережі. Було дешевше спочатку завантажити всі потрібні сайти, піти в офлайн і вже там продовжити читання абсолютно безкоштовно. З часом потреба в подібних рішеннях стала падати, і робота над кешуванням в тих чи інших продуктах якщо не припинилася, то вже точно тривала не в напрямку офлайн.



Сучасні мобільні браузери навпаки зіткнулися з проблемою офлайн в повній мірі. Ні, за час, проведений в інтернеті платити тепер не потрібно, але користувачі мережі самі стали мобільні і в будь-який момент можуть вийти із зони покриття. Якихось радикальних рішень цієї проблеми в популярних продуктах нами помічено не було. При цьому в мобільних збірках Chromium під Android (нагадаємо, Яндекс.Браузер використовує його у своїй основі) процес кешування представляє з себе сильно спрощену версію з десктопа. Ніякої складної логіки. Всі закэшированные ресурси живуть в одній черзі і видаляються у відповідності з алгоритмом LRU (видаляються ті елементи, які не використовувалися найдовше). Сам кеш представляє з себе «звалище» з окремих ресурсів (html, css, png, js, ...) з ємністю близько 300 МБ. З точки зору кешуючого алгоритму ці ресурси ніяк не пов'язані між собою і видаляються незалежно один від одного при нестачі пам'яті для нових сторінок.



Така спрощена логіка стала для нас проблемою, оскільки не дозволяла досягти передбачуваного результату при роботі в офлайні. Простіше кажучи, браузер не знає, що саме є у нього в кеші і наскільки цього достатньо для повноцінного відображення сторінки. Ми могли скористатися механізмом, реалізованим у рамках експерименту chrome://flags/#show-saved-copy. Якщо ви не знаєте, про що йде мова, то ось коротка суть. У разі відсутності мережі користувачеві пропонується спробувати завантажити сайт з кешу. Адже це рівно те, що нам і потрібно, що б ви думали!



На жаль, якщо включити цей експеримент, браузер перевіряє наявність локальної копії лише головного HTML-документа без урахування всіх інших ресурсів і скриптів. Для простих текстовий сторінок цього зазвичай вистачає. Але для всього іншого результат часом дуже сумний. А незабаром ми дізналися, що для багатьох популярних сайтів експеримент не спрацює навіть при наявності кеша, але про це трохи нижче, а поки що нам потрібно було навчитися прогнозувати результат.

Кешування прогнозоване

Ми хотіли домогтися більш якісної і ненав'язливою роботи в офлайні. Щоб користувач міг отримати доступ до вже і так завантаженої на його пристрій інформації без зайвих кнопок. Причому браузер має його попередити, якщо деякі ресурси вже були видалені з кешу. Для цього Яндекс.Браузер повинен був знати, чи доступні в кеші всі ресурси, що присутні на запитаної сторінки.

Теоретично ми могли перевіряти доступність HTML-документа в кеші, а далі вже пробігтися по всіх залежностях з нього і оцінити доступність кожного ресурсу. На жаль, такий простий спосіб оцінки «в лоб» не може похвалитися швидкістю. Так і на споживанні ресурсів це позначається особливо сильно. Настільки сильно, що холодними зимовими вечорами ваш смартфон міг би вас зігрівати. Ось тільки за вікном вже літо.

Подібні невдалі ідеї поступово привели нас до думки, що без структурних змін в кеші результат не досягти. Тому ми зайшли з іншого боку. Навчили браузер вже на етапі завантаження сайту запам'ятовувати залежності між сторінкою та її ресурсами. Всі ресурси, які необхідні для відображення конкретної сторінки, тепер логічно об'єднуються браузером в набір даних. Такий набір ми назвали «кластером». Причому якщо ресурс присутній на двох різних сторінках (наприклад, логотип сайту зазвичай дублюється), то він буде асоційований з двома різними кластерами. При цьому браузер може очищати кеш тільки за рахунок тих ресурсів, які належать самому неиспользуемому кластеру (той же LRU, але на рівні кластерів, а не окремих ресурсів).



В той момент, коли користувач запитує сторінку, Яндекс.Браузер вже знає якого кластеру відповідає запитуваний адресу, і які ресурси для неї необхідні. А далі він просто перевіряє доступність кожного елемента з кластера. Якщо кластер (тобто фактично всі ресурси потрібної сторінки) доступний повністю, то можна не лякати користувача помилкою, а відразу показати сайт, попередивши, що він працює в офлайні. Якщо частина ресурсів вже видалена, то у цій збірці ми також відновлюємо сторінку, але попереджаємо про те, що копія частково збережена.



До речі, на скріншоті один з п'яти експериментальних дизайнів для планшета, які також тестуються в поточній бете.

Кешування якісне

Коли ми говоримо про роботу в офлайні, то маємо на увазі не тільки банальне відновлення вкладки при запуску браузера. У нашому випадку кеш використовується для будь-якого способу відкриття сторінок. Ви можете натиснути «Оновити» або переміщатися по історії через «Вперед»/«Назад». Можете тапнути по посиланню. Можете вручну ввести адресу. У всіх цих ситуаціях Яндекс.Браузер оцінить доступність ресурсів і завантажить локальну сторінку, якщо це можливо.

Ми й раніше підозрювали, що далеко не кожен сайт може бути відновлений з кешу. Але тільки після того, як навчилися прогнозувати результат, стало ясно, що про повноцінної і якісної роботи в офлайні без подальших доопрацювань можна забути. Багато сайтів ніколи не відкриються в офлайні. Наприклад, Яндекс, Хабрахабр, Facebook або ВКонтакте (якщо ви маєте прав на них).



Причина криється в HTTP-заголовку «cache-control:no-cache, no-store», який забороняє браузеру зберігати ресурси в кеші. Ми розуміємо, що вебмастери використовують його у тому числі для того, щоб регулярно оновлювані ресурси не бралися з кеша, а завжди завантажувалися самої свіжої версії. На жаль, широко поширена практика додавати цей заголовок не для окремих ресурсів, а для всього HTML-документа, що ставить хрест на відкритті сайту в офлайні.

Відмовлятися від офлайн ми точно не збиралися. Але і простим ігноруванням зазначених HTTP-заголовків проблему вирішувати не можна було. Це могло призвести до використання в онлайні неактуальних ресурсів з кешу. Нам потрібно було дотриматися сенс HTTP-заголовка, але при цьому забезпечити доступ до інформації без з'єднання з мережею. На щастя, нашим розробникам вдалося знайти таке компромісне рішення. Згідно з цим рішенням, Яндекс.Браузер зберігає в кеші всі ресурси, включаючи ті, для яких прописана заборона через «cache-control». Але при цьому ми зберігаємо інформацію про наявність заборони та не використовуємо такі ресурси при звичайній роботі в онлайні. Браузер звертається до них тільки в офлайні. Більше того, ми зараз досліджуємо можливість шифрувати цю частину кеша.

До речі, результати POST-запитів або виконання AJAX ми також зберігаємо в кеш. Без цього багато динамічні сайти виглядали б в офлайні неповноцінно.

Перспективи кластерного кешування

Повертаємося до наших кластерів. При звичайному кешуванні всі збережені ресурси ніяк не пов'язані між собою і видаляються відповідно до LRU. При цьому ніяк не враховується, відкрита вкладка зі сторінкою досі де-небудь в тлі, або користувач вже давно її закрив. Щоб краще зрозуміти проблему, розглянемо простий приклад. Ви відкриваєте в браузері на своєму смартфоні пост на Хабре і залишаєте його відкритим, щоб прочитати на борту літака. Але до того моменту, як почнеться посадка на рейс, вам може знадобитися поглянути на прогноз погоди, перевірити нові повідомлення в соціальній мережі або попрацювати з веб-версією пошти. Так, вкладка з постом на Хабре у вас все ще відкрита в тлі. Але її ресурси вже встигли перейти в кінець черги і були видалені з кешу. Та й сама вкладка вже були викинута з оперативної пам'яті. І ось ви летите в 10 тис. метрів над землею, дістаєте свій телефон і клікаєте по потрібній вкладці, передчуваючи захоплююче чтиво. Але браузер видає помилку, тому що нещадному LRU немає справи до відкритих вкладок та ваших проблем.

На щастя, у нас вже винайдені кластери, які можуть вирішити цю проблему. Запровадження кластерів дозволило нам об'єднувати ресурси в логічні групи, кожна з яких явно асоціюється з певною сторінкою. Щоб вирішити описану вище проблему, досить забороняти браузеру видаляти ресурси тих кластерів, які відповідають відкритих вкладок. А можна піти ще далі і передбачити можливість вручну зберігати сторінку. Саме над цими ідеями ми зараз і продовжуємо працювати.

На жаль, навіть кластерний кешування на даний момент не може гарантувати доступ до сторінки в офлайні. Якщо ви раптом завантажуєте важкий сайт, то його ресурси можуть дуже швидко вибити з кешу всі інші. Але в більшості випадків наша технологія дозволяє уникнути незручних моментів і надати користувачеві доступ до завантаженої інформації. І як би ми не старалися, сайти в офлайні далеко не завжди працюють саме так, як в онлайні, тому ми обов'язково інформуємо користувача про відсутність мережі спеціальною іконкою.

Спробувати офлайн-режим ви можете в бета-версії Яндекс.Браузера 15.6 для Android. Уже в цій збірці браузер вміє відкривати сторінки в офлайні, якщо необхідні ресурси доступні в кеші. Запрошуємо всіх бажаючих допомогти нам з пошуком проблем і нових ідей.

P. S. Версія для iOS була практично готова, але нещодавно ми приступили до міграції на новий компонент WKWebView, який, на жаль, позбувся багатьох можливостей у порівнянні із застарілим UIWebView. І нове кешування там поки не реалізувати по ряду причин. Але це вже зовсім інша історія.

P. P. S. до Речі, якщо ви хочете допомогти нашій мобільного команді з винаходом інших цікавих технологій, вакансії чекають. Ми шукаємо розробників (C, C++, Java), дизайнерів, керівників проектів, фахівців з продуктів і тестувальників в офіси Яндекса в Москві, Санкт-Петербурзі і Новосибірську.

Джерело: Хабрахабр

0 коментарів

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