Галоўная / Артыкулы

Як сховішча браўзера непрыкметна псуе даныя карыстальнікаў і як гэтага пазбегнуць

Сховішча браўзера можа псаваць даныя без памылак: страчаныя абнаўленні, састарэлыя здымкі, дрэйф схемы і агульныя прылады. Патэрны для localStorage, якія вытрымліваюць.

Выдавец: REACTAPP.TOP14 хв чытання3109 слоў

Крыніцы: The Browser Storage Mistake That Quietly Corrupts User Data

Зберагачча браузера выглядае прыгожа простаю. Серыялізавайце об’ект за дапамою JSON.stringify, прачытайце яго знову за дапамою JSON.parse, і праганні корыстувальніка, часткова заполнена форма, фільтры чыста праекты застаюцца пасля наступнага завантажэння. Няма схемы, няма запыту да сеткі, няма міграціі на бэкэнд. Калькі лінійак JavaScript можа зробіць, што прыложэнне будзе вярнае прыблізна за калькі хвілін.

Гэтая простасць спрыяе рызыкаванай думцы: што все, што зберагаецца ў браузеры, можа вважацца маленькай локальной базай дадзеных.

Проблемы зазвычай пачынаюцца з адного вялікага об’екта стану. Прыложэнне завантажае ўсё гэта з localStorage, зміняе якісь атрыбут у памяці і знову запісвае ўсё гэта. Кожны крок здаецца разумным. JSON ў правільным фармаце, запіс выходзіць успешна, а на экране показваецца апэктыўная значэнне. Нічога не здаецца не так.

Проблемы выявляюцца тады, калі іншая частка прыкладнення зберагае застарэлую копію таго ж об’екта. Можа, другі вікніце зменяе настройкі, фонавы працэс рэдагуе чэрнавку, а пярэднія версія прыкладнення інакша адначыта гэты об’ект. Можа, компанент практычна адразу пасля таго, як корыстнік чыгае якісь змяны, запускае старыя даны. Паколькі кожны з гэтых элементаў зберагае цэлы скрыншот, змена аднаго поля можа тыха вярнуць старыя значэнні ў всіх іншых полях.

Не патрабуецца жадных сінтаксічных памылак, і не выклікаецца жадных асабліванняў зберохвату. Кожны элемент можа зберагаць абсалютна правільныя даны, а ў той жытак стэрць правільную апдэйт другога элемента.

API для зберагча ня ўсё-такі ў вінаватасці. Памылка заключаецца ў тым, што спакушаюць звычайны хранальнік стрэлок, у яком пасляпэднія зберагчыце прымае перавагу, паводзіцца так, нібы ён знае пра запісы, поля, транзакцыі, версіі, прыналежнасць і адночасны доступ. На самай працы ён нічога з гэтага не знае і проста памятае ту стрэлку, якая была прызначана клучу ў пасляпэдні раз.

Невінаваты спосаб чытання-змены-зберагчыця

Тыповая рэалізацыя выглядае так:

function updatePreferences(changes) {
  const saved = localStorage.getItem("user-settings");
  const settings = saved ? JSON.parse(saved) : {};

  const nextSettings = {
    ...settings,
    ...changes,
  };
  localStorage.setItem(
    "user-settings",
    JSON.stringify(nextSettings)
  );
}

За дапамою аднаго вікна і аднай чыннай версіі прыкладнення яго можна вікорыстоўваць гады. Кожная апдэйтаванне чытае найсвежэйшыя значэнні, з’еднае змяны і запісвае рэзультат. Гэта кароткая, чытальная і зазвычай дастаточна для базовых настаўкі.

Рызык падчасы, калі між чытаннем і запісам прымыкае час, або калі прыкладнення зберагае копію об’екта ў памяці.

Падазроўваючы, што два вікна завантажаюць тыя ж настаўкі:

{
  "theme": "light",
  "fontSize": 16,
  "sidebarCollapsed": false
}

Першая карточка пераключае тэму на тэмную. Другая збільшае розмер шрыфту да 18. Калі кожна з іх правіць свой власны копію ў памяці і зберагае весь об’ект, той, хто зберагае пасля, стырае змяны іншага.

Першая карточка можа зберагаць гэта:

{
  "theme": "dark",
  "fontSize": 16,
  "sidebarCollapsed": false
}

Пазней другі карточка, якая яшчэ зберагае старэйшы скрыншот, зберагае гэта:

{
  "theme": "light",
  "fontSize": 18,
  "sidebarCollapsed": false
}

Змена розмеру шрыфта застаецца, а тэма тыха прыходзіць у светлы варыянт.

Гэта наследкі неякоснае апдэйта. Канечны об’ект не ўражаны, і кожна польа маюць правільныя значэнні. Проблема заключаецца у тым, што адні з правільных апдэйтаў знік без жадных азнаак канфлікту.

Гэтыя багі захоўваюцца падчас разработкі, таму што распрацоўшчыкі тэстуюць аплікацыю па аднам вікну, аднам прыстрою і адной дзеянні за раз. Рэальныя корыстувальнікі трывалы часы залішаюць аплікацыю відкрытай, ствараюць дублікаты вікон, вярнююць сеансы, перыядаюць вікна і вяртаюцца на сторункі, якія ў памяці маюць старэйшы стан, чым той, што зараз збераглі.

API для зберагання не ведае, што адна вікно зменіла толькі тэму, а іншая — толькі розмер шрыфта. Ён бачыць два цэлыя строкі, і перамагае апошній з яных.

Снімак не ўособлівае тэкушчы стан

Сяроджытнейшая памылка — гадка, што об’ект, завантажаны з хранення, застаецца на звязку з храненням. Гэтага не выходзіць. JSON.parse стварае значэнне ў памяці, якое на аднам моменце апісвае зберажаны рэчып. Пасля чаго гэта проста копія-знімак. Тым часам іншая вікно можа апдэйтаваць ключ, компонент можа яго заменіць, або новейшая версія прыложэння можа перэнесці яго, а копія, якая ўжо ў памяці, нічога пра гэта не ведае.

Кераванне станам ускладнюе сітуацыю. Прыложэння можа запускацца з localStorage і пасля кожных змян зберагаць увесь стан. Калькі компоненты працуюць з гэтым знімком, пакуль званэрныя элементы моць зменіць той самы ключ.

Задзейнаваныя функцыі зявлення спрыяюць падышку часу зберагачвання дадзейнаў. Большасць дапытакаў блокуюць паўтаральнае зберагачванне, таму localStorage.setItem не выконваецца пасля кожнага натыку клавішы. Корыстнік рэдагуе поле, зберагачванне запланавана на калькі сотых секунды пазней, і функцыя-закрыцце сохраняе об’ект стану з таго моменту. Да таго часу, калі адбудзецца зберагачванне, іншы вікніцы або компаненты могу зберагчыць новейшыя даны. Калі нарэшце выконуецца функцыя-адказ, яна запісвае свой старэйшы копію над новейшымі значэннямі.

Функцыя зявлення паспрабавала паспрабаваць павышыць працэсавую скорасць, але пагубіла адносна стабільнасць, адколі ніхто не задаў правіл на тое, што робіць з застарэлымі копіямі дадзейнаў.

Асінхронная ініцыяцыя дае аднаковы рэзультат. Компанента атрыбутуецца стандартнымі значэннямі, корыстнік можа негараздзе з нею взаімадзеяваць, а зберажаны стан прыходзіць пазней праз эфект фрэймворка або абстракцыю. Пасля чаго крок гідратацыі заменяе ныяны стан на старэйшы зберажаны фотакап. Для корыстніка выглядае так, нібы інтэрфейс анулюе яго змяну чырвоны пасля таго, як яна была здабыта.

Такія багі здаюцца адказальнымі, таму што кожна з участвуючых значэнняў ёсць правамернымі. Асалідныя проблемы — гэта часовая прыорытэтнасць: чы хаця быць пераважным раней занесенам значэнню, пазнейшая дзеянне корыстніка, чы значэнне, занесеннее ў іншым кантэксце? Калі няма чытальных правілаў, тады тое значэнне, якое выканаецца апошнім, віршае пытанне.

Перазберагаецца без транзакцый

localStorage зазвычай апісваецца як сінхронны хранальнік ключ-значэнне. Гэта правды, але гэта можа стварыць большая доверлівасць, ніж API заслуговае.

Адзін вызыв setItem сінхронна заменяе значэнне ў чырвонай сэрэге. Гэта атамічнасць стосуецца толькі аднаго вызыву. Перадача дадзеных праз чытанне, зміну і запіс у калькі крокаў не ўважаецца транзакцыяй між вікнамі аб працоўнікамі. Не існуе функцый паўтарнага вылічэння і замены, блакавання рэкі, канфліктавання версій, з’еднання поль і анулювання змян.

Концэптуальна, парадокс застаецца такім:

const current = read();
const next = modify(current);
write(next);

Іншы кантэкст можа выконваць свой сэрыё задаўнення ўзьмісцам іншага скрапшота. Кожна чытанне і запіс выходзяць успешна, але сумарны рэзультат прыводзіць да втраты дадзэнняў.

Раздзелэнне катэгарыяў на адзінаковыя ключы зменшае канфлікты. Тэма, якая зберагаецца пад аднім ключам, не можа перазапісаць неякі іншы, некалякія праект пад іншым ключам. Гэта лепшая, чым зберагаць адзін вялічэзны об’ект стану, хоць гэта не рашае всіх проблемаў канкурэнцыі, адколі два корыстувальнікі все ўсё можу борацца за той самы праект або запіс прыямоў.

Браузер таксама выклікае змест storage, каб паведаміць іншым документам, калі змініваецца ключ localStorage. Гэта дапамагае у сінхронізацыі, але гэта не механізм транзакцый. Калі іншая вкладка отрымае гэты змест, яна можа ужо выконаць дзеяння з застарэлым станом або запланаваць запіс, які вярнуе ўсё да старай версіі. У змесцы фіксуецца, што адбыўся ўсё новы запіс. Аплікацыя яшчэ должна вырашыць, як узгодзіць гэта з незахаванымі локальнымі змянамі.

Тут багато рэалізацый становяцца складнымі. Распрацоўшчыкі дадаюць слухачы, з’еднаюць об’екты, апдэйтуўваюць базы дадзеных і стрымваюць цыклічныя запісы, і незабоў жа ствараюць частковую сістэму сінхронізаціі на базе API, выбранага таму, што ён здаваўся простым у пораўнанні з рэальным шаром дадзеных.

Нічыя з гэтых пунктов не значыць, што localStorage не павінен хаваць даныя прыкладнення. Гэта значыць, што гарантыі, якія вам патрэбны, должны супараджавацца з тым, што дае API. Для маленькіх значэнняў з явным власнікам і можлівасцю перазапісу, гэта ўзгодна. Але калі калькі актора незалежна рэдагуюць адну спільную, структураваную запіс, стан прыкладнення становіцца кранічна нестабільным.

Адна вялікая маса JSON мае вялікі радыюс уражэння

Зберагчы весь стан пад аднім ключам ёсць зручна, таму што працэс ініцыялізацыі прыкладнення выконваецца за адну чытанне і парсаванне. Але гэта таксама значыць, што кожная апдэйт перазапісвае весь стан.

Змена настройкіх бокавайну прыводзіць да перапісвання ўсіх іншых элементаў, якія дзеляць той жа блоб: незаканчаныя праекты, метаданы абмовкі, апошнія пошукі, адхіленыя банеры, значэнні форм, якія знаходзяцца ў кэшы. Калі аўтар заставіць старыя даны хоць для аднаго з гэтых элементаў, несувязаная змена вярнуе яго старое значэнне. Чым больш тэмаў аб’еднанае ў аднай ключоўай зменнай, тым больш верагатыва, што адні аўтары не бачылі змян іншых.

Міграцыі таксама страждаюць. Новая версія можа пераформатаваць частку об’екта і заставіць рэшту без змян. Калі міграцыя не завершана, прыстанок можа запісаць об’ект, які сумішвае поля з разных версій, і старыя табы пазней можу зберагчы свою пярвінную структуру, фактычна скасаваўшы міграцыю.

Раздзелэнне сховішча па прыналежнасці і цыклу жыцця зменшае гэтыя суперсечанні. Няхіба прыямкі тэм, тымчасовыя чэрнавікі, адхілёваныя паведамленні і элементы кэша павінны дзеліць адну версію, правілы тэрміну давання чы роцэ запісу. Незалежныя ключы ускладнююць сітуацыю, калі змяны ў однай частыні можу страціць значэнні іншай.

Раздзелэнне таксама не павінна быць механічным. Стварэнне кожнага ключа для кожнай дробнай характэрыстыкі стае неконтрольным, і гэта можа прыводзіць да суперсечных комбінацый, калі калькі значэнняяў павінны змяніцца адночасова. Хорашыя межы — гэта стан, які мае аднаго власніка, адну схему і адна правіла для апдэйтаў. Значэнні, якія павінны застацца адносова стабільнымі як едынця, могу належаць да адной групы. Значэнні, якія зміняюцца з незв’язаных прычын, не отрымаюць нічога ад спільнага зберагачэння, а толькі дадаюць дапамогу аднаму выкліканню проблем.

Спачатку трэба думаць пра ключы як пра срэдства для зберагачэння адной цэліснай інформацыі. Ён не павінен заменяць усю памяць прыкладнення.

Змена схемы прыводзіць да таго, што старыя даны стаюць новымі збоямі

Даныя браузера часта застаюцца актуальнымі дыяграма часу. Пользоватэль можа застаўляць той самы профіль пры дзесятках выданняў, а значэнні, занесеныя старай версіяй, могу з’явіцца зноў праз калькі месцаў. Таму ў запамяці браузера існуе схема, нават калі ніхто яе не праектаваў.

Спадзяймося, старэйша версія зберагла булева значэнне:

{
  "notifications": true
}

Новейшая версія заменяе яго на болейш насыцаны об’ект:

{
  "notifications": {
    "email": true,
    "push": false
  }
}

Код, які вырачае новую структуру без перагляду чыста перайменавання старых значэнняў, можа выклікваць проблемы. Але ўсё болей падступна, ён можа выкарыстоўваць стандартныя значэнняі пазней зберагаць іх замест тых, якія выбраў корыстнік.

Перайменаваныя поля таксама вызываюць падобныя прыбылкі. Застаўшыся флаг darkMode можа існаваць праза новыя значэнні theme. Одна частка прыстрою чытае новае поле, старыя часткі запішуць старую структуру, і корыстнікі бачаць, як іх настройкі непрадбачальна зменяюцца між сесіямі.

TypeScript не рашае гэтых проблем. Кампайляр можа опісаць тэперашнюю апэктыўную структуру, але JSON.parse вяртае даныя, створаныя невядомым кодам у мінулым. Асэрцыя тыпу заглушае кампайляр і ускладняе выяўленне несувярэннасці:

const settings =
  JSON.parse(savedValue) as CurrentSettings;

Эта лінія не ператварае старыя даныя зберагача ў чынныю схему. Яна проста прыказвае TypeScript паверыць ім.

Больш безпечны код спрацьвоўвае з зберажанымі значэннямі як з зовнішнім вхідным дадзенням. Ён ўзламвае іх, пераканальвае ў правільной структуры, пераглядае версію і намеравана мігруе вядомыя старэйшыя фарматы. Калі значэнне не можа быць безпечна адгукнута, прыкладанне павінна выбраць: або адмовіцца ад яго, або заставіць яго для вярнення, або запытаць корыстніка.

Номер версіі, які зберагаецца праз тыя ж даныя, чытра паказвае пераходы:

{
  "version": 3,
  "data": {
    "theme": "dark"
  }
}

Номер версіі сама пацэўка міграцыі не выконвае, але ён паведамляе коду, якія заставы яшчэ чынныя.

Успешныя практыкаванні — гэта не абавеснае

Распрацоўшчыкі часта абгортваюць JSON.parse у try/catch і вызываюць слой зберагача захішчанаўна. Гэта дапамагае распрацаваць некарэктны JSON, але сінтаксічная правамернасць — толькі першая патрабованне.

Аб’ект, які быў распакаваны, можа не мець неабходных поль, мець немагчымыя значэнні, следаваць застарэламу шыму або быць рэкамендаваным рукамі за дапамою інструментаў распрацоўшчыка або расшырэння. Значэнні таксама можу мець правільны тып, але неправільны значэнне. Размер стораніцы, зберагнуты як -500, ёсць дазволеным чыслам у дазволеным JSON. Тэма пад назвай "ultraviolet" ёсць дазволеным строкам. Чорнавік, які не мае ID, які яго прыяўязуе да дакумента, ёсць дазволеным аб’ектам. Ніхто з іх не заслуговае на аўтаматычнае доверлеўце як стан прыкладнення, таму што браузер зберагчы строкі і нічога не ведае пра правіла вашага домэна.

Надзеяны працэс інтэграцыі пераканальвае, што прыкладненне гатова прыняць, выкарыстоўваючы бібліятэку шымоў, невялікі рукамі напісаны распакавальнік або рэтэльна спраектаваныя канстрактары. Галоўнае — ў процэсе пераканальвання ствараецца вядомая внутраняя модэль, а не працоўваюцься сырыя даныя, распакаваныя з вхіднага тэксту, у прыкладненні.

У процэсе атрыбутацыяя таксама выкорыстоўваецца стандартныя значэнні. Няяўнае необавязковае настройкаванне можа без апасцярыбыць быць замененае стандартным значэннем. Няяўны ідэнтыфікатор у незахаванамым праекце можа значыць, што гэты праект не можа быць прыўязаны да жаднага дакумента і не трэба яго аўтаматычна восстанавляць. Адночаснае спрыяванне як стандартным, так і няяўным значэнням стварае хыбную адчуванню безпекі, якая маскіруе проблемы з дадзеннямі. Альтэрнатыва не должна маскаваць неяснасці. Калі прыстаноць не можа паказаць, што зберажаныя даныя належаць тэкушчаму корыстніку, схеме чы рэкорду, ўсуненне гэтых проблем можа завдаць большай шкоды, чым іх ігнораванне.

Ідэнтыфікаторы должны быць часткай кантракту зберохвату

Спільныя прыстаноці ствараюць ўсё новую хыбную дактрыну, што зберохват належыць таму, хто ўвійшаў у сістэму. Насамперад, зберохват належыць самой прыстаноці, якая яго выкарыстоўваець.

Уявіце, калі адзін чалавек выходзіць з аплікацыі, а другі заходзіць у яе на той самы камп’ютар. Залішаныя чэрнавікі, фільтры, настаўкі чы рэкорды з кэшу застаюцца, калі толькі сама аплікацыя іх не ачышчае чы не прызначае ім свае іменаванні. Універсальныя ключы, такія як draft, recent-items чы dashboard-settings, могу пераказаць даныя першага чалавека ў сесію другога. Нават калі тут няма нічага супернаватковага, гэта спрыяе парадоксальным наследкам, адколі правільныя даныя прыкрэпляюцца да няправільнага корыстніка.

Ачышчэнне всіх дадзеных пад час выходу з аплікацыі дапамагае, але ён ёсць неадэкватным спосабам. Гэта можа адмахнуць легітымныя настаўкі на рэвэле прыстрою, такія як настаўкі мовы чы доступнасці, а таксама можа не спрацаваць, калі выхід з аплікацыі відбываецца ў іншай вікні, а ныякая вікні застаецца з кэшаваным знімкам у памяці.

Болейш адаптаваны дызайн сортавае даныя на даныя, прызначаныя для конкрэтнага карыстальніка, сесіі, прыстрою і для загальнага вжытку. Запісы, якія належаць карыстальніка, можу мець яго ID у ключы або ў об’екце-обгортцы, а процэс пераканання можа паўніста парадактаваць, чы рэзерваваны власнік збігаецца з ідэнтычнасцю, з якой адбыўся вхід, прыймаючы значэнне.

Часта для праектных версій таксама патрэбны ID дакумента або рэсурса. Вярненне „пасляпэльнай версіі“ на глобальны лэвэл ёст крыхкім, калі відкрытыя калькіляцыі, адколькі контэнт з аднай версіі можа з’явіцца ў іншай, калі ключ не вказваў, які об’ект яго належыць. Называнне прастора — гэта не проста парадакт, а спосаб перашкодзіць таму, каб правільныя даныя потрапілі не туды.

Шыфраванне не вылечвае парадкі

Калі гаворыцца пра рызыкі, якія ствараея хранэнне дадзейнаў у прыгледачах, часта адразу згадваецца безпека. Чуліватая інфармацыя не должна лежаць без адпаведнага адзыркання ў хранэнні, доступным прыгледачу, а ўсё болей там, дзе скрыпты, якія можна запусціць, могу яе прачытаць. Оось справжняя проблема, але яна не адносіцца да пытання адпаведнасці.

Шыфраванне збераганых дадзейнаў нічым не памагае запобiec перазапісву аднае другай карточак. Яно не дае можлівасці адстаўляння транзакцый, пераканання версій, візначэння аднойчыннага власніка чы ўраджэння новых дадзейнаў. Програма можа без проблем шыфраваць застарэлы фотакопію дадзейнаў, а пасля таксама без проблем знішчыць новейшую версію. Команды іноды вяртаюцца да кодавання, заслонення чы шыфравання, каб рашыць проблему, якая на самай справе ёсць проблемай дызайну. Гэтыя заходы можа дапамагаць парадзіць конфідэнцыяльнасці ў певных мадэлях абараны, але працэс апдэйта застаецца такім жа ненадзеяным.

Адзінакова справа з пераходам з localStorage на IndexedDB — гэта можа даць большую ўместнасць, структураванае зберагачванне, індэксаванне і можлівасць абавяння. Аднак сама по сабе гэта тэхналогія не стварае правільнай логікі прыкладнага праграмы. Абавянне павінна охопляць правільныя запісы, абнавленні яшчэ павінны адпавядаць прынцыпам власнасці, а абнаўленні схемы таксама трэба спраектаваць. Тэхналогія працюе з моцнейшымі прымітывамі, і прыкладна праграма павінна іх вікорыстоўваць па чыстаму модэлю консыстэнтнасці.

Пачніце з власнасці

Уместа таго, каб спытваць, кой API зберагачвання выбраць, спачатку трэба з’ясавіць, хто ўладае дадзеннямі і як трэба адмаўляцца змянам іншага корыстувальніка.

Звычайныя настройкі часта можна працаваць за прынцыпам «пасляльнейшая змена — гэта правільная». Калі два вікна задаюць разныя тэмы, можа быць достатнім заставіць самую няўялікую, але прыкладная праграма все раве павінна сочыць за змянамі ў іншых вікнах, калі візуальны інтэрфейс павінен негайна абнавіцца.

Незаканчаны ўказка — гэта ўжо іншая справа. Пры прымэтцы, калі першы, хто запісаў, выграе, корыстувач можа паспацьваць на сэрьёзныя наследкі, таму дакументу можа знадобіцца номер рэвізіі, час запісу, ідэнтыфікатор дакумента і правіла разв’язвання суперсчытанняў. Перш чым заменіць даны, прыстаўка можа параваніць рэвізіі, захаваць обе версіі, якія суперсчытаюцца, або павесты корыстувача, што іншая вікно зменіла той самы ўказка.

Для важлівых дакументаў, якія дзеляюцца межа прыстаўкамі, храненне ў браузеры ўжо зовсім не павінна быць аднакова значымым джэрэлам інфармацыі. Ён можа служыць офлайн-кэшам або тымчасовай рабочай копіяю, пакуль сервер зберагае автантыфікованыя версіі і разв’язвае суперсчытанняў.

Правільны дызайн залежыць ад таго, наскількі ценныя даны і скількі людзей іх зміняе. Параваніце праграму показу дакументаў з юрыдычным формам, длігім праектам статты або офлайн-фінансавым дакументам — рызыкі ў іх явна розныя. Адносіцца да ўсіх іх як да строк пад довільнымі ключамі значыць ігнараваць наследкі втраты адной з апдэйтаў.

Адзін шлях для зберагчэння таксама дапамагае. Калі кожны компонент можа чытаць, з’еднаць і заменіць тую ж ключоўую значэнне, калі толькі хоча, ніхто не можа пераглядаць правіла адпаведнасці. Адзін модуль зберагчэння можа цэнтрызаваць праверкі, міграцыю, сінхронізацыю і адрабатаванне канфліктав. Для гэтага не трэба велікага шару хранення даколкі трохі настаўк. Гэта проста не дазволяе коду для пастановкі дадзеных распрастраņвацца настолькі, каб ніхто не могаў сказаць, якае з кадраў ёсць актуальным.

Зберагчыце дадзеныя, не перыяктна іх пераадначвайце

Найбольш небяспечныя багі ў зберагчэнні не паведамляюць аб сабе праз QuotaExceededError чы паўстановку JSON. Іх можна побачыць, запісаць, працаваць з імі і адрабатваць. Найбольш шкодзіць тады, калі запіс выходзіць успешны, але зберагаецца некорэктная версія іншым чынам правільных дадзеных.

Уявіце саблон, які выправляе адзін поль і вяртае пяць старых, запаздзелы ўтварэнны калбэк, які змянюе новейшы праект, старую версію, якая зберагае застарэлую схему, або новы працэс заходжання, які успадковвае стан пярэдньага абанку. За дапамою API для зберагача кожна з гэтых операцый выглядае як успешная. Аднак парадокс выступае там, ўжо ўскладненны тым, што прыложэнне ніколі не візначала версій, власніка чыста правіл выкарыстоўвання пад час конфліктавання.

Зберагач браузера становіць ся корыстным тады, калі яго меры падходзяць для пэўных задач: зберагаччы настройкі, кэшаванне нечыста важлівых дадзеных, падтрымка працы без падключэння да інтэрнэту, вярнэнне незаканчанай работы. Але калі якісь даныя застаюцца у зберагачы, ўскоры з’яўляюццася проблемы, таму ўсё гэта трэба ставіць на той жа ранг, што і даныя з сервера чыста базы дадзеных.

У практыцы гэта значыць перакантрольваць тое, што вяртаецца, стварыць версіі, трываць несувязаныя запісы окола сябе, дадаць інформацыю пра аідэнтычнасць карыстальніка і рэсурса там, дзе важна прыналежнасць, а таксама не перапісваць вялікія знімкі стану для маленькіх змян. Гэта таксама значыць свядома выбіраць, чыі правіла «першы, хто запісаў, той і выграў», ўбачліва, каб гэта не стало правілам аднойчынна.

Браузер будзе верна зберагаць последні тэкст, які он прыме. Забезпечыць, каб той, хто запісаў последні, дзеўнае права на рашэнне пра тое, што застоіцца, — гэта тое, што залічаецца на вас.