Перейти до вмісту

Блог ·

Утримання стає розміщенням

Переміщення машини між двома стійками має одну небезпечну мить: коли призначення вже не зарезервоване і ще не зайняте. Ось як ми її прибрали.

Автор: Provibr

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

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

Вікно, і як його не мати

Очевидна послідовність це три кроки. Звільнити стару заявку, звільнити утримання на час переміщення, яке лежить на призначенні, записати нове розміщення. Усередині однієї транзакції те вікно невидиме для інших сесій, і саме тому його легко лишити: воно пройде кожен тест, який ви напишете. Це однаково вікно, бо оператор після нього працює зі слотом, який звільнив попередній оператор, і одного неправильного порядку під час пізнішого рефакторингу досить.

Ми робимо простіше. Утримання на час переміщення підвищують, а не замінюють. Рядки, що записують зайняті юніти, лишаються рівно там, де вони є; змінюється лише вид заголовка, з утримання на час переміщення на розміщення, і тим самим оновленням його строк дії ставлять у null. Старе розміщення закривають у тій самій транзакції. Немає точки, у якій призначення вільне, тож немає другого шансу зіткнутися, і все це є одним оновленням, а не вставкою, яка мусить вдатися.

Ідентифікатор заявки через підвищення не змінюється, і це спосіб перевірити, що сталося справді це, а не щось, що ззовні лише має такий вигляд.

Ставити строк дії в null це не охайність. Перевірка в базі даних відмовляє розміщенню, яке його несе, бо розміщення, що тихо перестає рахуватися у вівторок, поки машина досі прикручена, є стійкою, у якій програма вважає вільним слот. Резервування, що спливає, є можливістю; розміщення, що спливає, є брехнею з таймером.

Арбітром досі є один індекс

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

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

Функція планування є повідомленням, а не захистом. Захистом є індекс. Саме розведення цих двох ролей дозволяє повідомленню бути корисним без того, щоб у когось виникла спокуса на нього покластися.

Девʼять станів, і два виходи, які перестають існувати

У переміщення девʼять станів: чернетка, заплановане, схвалене, у процесі, очікує перевірки, потребує звірки, завершене, скасоване й невдале. Те, які переходи законні, є записом по всіх девʼятьох, а не оператором вибору. Оператор вибору з гілкою за замовчуванням дозволяє щойно доданому станові провалитися в дозволене, а небезпечний напрямок тут саме такий: стан, який хтось додає, бо стається щось незвичне, був би першим станом, якому дозволено все.

Цікавим є рядок, що виходить із in_progress, бо з нього зникли два виходи, які має кожен попередній стан: немає шляху назад до схваленого й немає шляху до скасованого. У цій точці машина в когось у руках, і єдиними чесними виходами є «повідомлено про виконання», «ми не знаємо» і «не вдалося». Скасування є рішенням, яке ухвалюють заздалегідь; після того, як про фізичну роботу повідомили, відкат у базі даних є твердженням про фізичний світ, якого ніхто не перевіряв.

Три кінцеві стани, завершене, скасоване й невдале, несуть порожні списки, а не відсутність запису. Це різниця між «звідси нічого не йде далі» і «про це ніхто не подумав», і рішенням є лише перше.

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

Переміщення не є перевезенням

Активи несуть стан процесу, і ця робота додала moving поруч із наявним in_transit. Вони звучать як синоніми, але ними не є. moving означає, що машину переміщують усередині цього центру обробки даних і місце її призначення зарезервоване. in_transit означає, що вона в дорозі між двома майданчиками й тому ніде. Обидва блокують передачу клієнтові, але вони відповідають на різні питання, коли хтось питає, де машина, і злиття їх зробило б відповідь на друге питання неправильною. Це розділення не лише описане, а й примусово дотримане: перевірка в базі даних вимагає, щоб початковий і цільовий майданчики переміщення були тим самим майданчиком, тож переміщення за побудовою є тим, що стається всередині однієї будівлі, а дорога між двома майданчиками є іншим процесом з іншим словом для нього.

Те, який стан машина мала до переміщення, зберігають у замороженій попередній перевірці, а не припускають. Обладнання, яке щойно приїхало, сидить у staging, і повернення всього до normal після цього тихо виписало б кожну переміщену машину з приймання.

Два виходи з незнання, і жоден із них не є відкатом

Коли фізичний результат невизначений, переміщення йде в needs_reconcile, і хтось мусить піти й подивитися. Відповідей дві, і обидві записують як спостереження.

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

Друге навмисно є невдачею, а не скасуванням. Хтось зробив фізичну роботу, і машина не переїхала; це результат, а не вибір. І це не відкат: базу даних не повертають у стан, який удає, ніби нічого не було. Історія розташувань є такою, що до неї лише додають, виправлення є новими компенсаційними записами, і різниця виявляється через місяць, коли хтось питає, чому машина цілий день стояла на місці. Відкат не лишає по собі нічого. Компенсаційний запис показує, що була невизначеність і як її владнали.

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

Два невеликі рішення, які матимуть значення пізніше

Переміщення вказує на свою фонову операцію посиланням, яке стоїть на SET NULL, коли операцію видаляють, а не на CASCADE. Операції підчищають через девʼяносто днів. Адміністрування обладнання не підчищають, і каскад означав би, що політика зберігання для смужок прогресу тихо видаляє запис про те, куди поїхала машина.

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

Що ми записали б

  1. Підвищуйте, а не створюйте заново. Якщо резервування й розподіл мають ту саму форму, змінюйте вид, а не звільняйте й пишіть, і перевіряйте, що ідентифікатор вижив.
  2. Тримайте арбітра в базі даних, а пояснення в коді. Індекс вирішує, хто виграв; функція планування каже, які юніти конфліктують.
  3. Пишіть таблицю переходів по всіх станах. Гілка за замовчуванням перетворює новий стан на дозволений.
  4. Після повідомлення про фізичну роботу ніколи не відкочуйте. Додайте стан, що означає «невідомо», і дайте йому два задокументовані виходи, обидва спостереження.
  5. Зробіть історію такою, що до неї лише додають, і хай виправлення будуть новими рядками, бо сам факт, що колись була невизначеність, є інформацією.
  6. Перевірте, до чого привʼязана ваша політика зберігання. Каскад із короткоживучої таблиці в довгоживучу є помилкою втрати даних із розкладом.

Заради ясності, чим це є: робота над логістикою центру обробки даних лежить за прапорцем розгортання, який типово вимкнено, а поверх нього налаштування на кожну організацію, тож нічого з цього сьогодні не купити. Чотири речі недоведені. Живу подію прогресу для переміщення випробувано без її шини повідомлень. Скасування переміщення з операційного екрана не проганяли в браузері. Утримання, яке справді сплило, ніколи не спостерігали, і навмисно немає жодного фонового проходу, який його очистив би, бо утримання, що прибирає себе саме, поки хтось стоїть біля стійки, є рівно тією суперечністю, задля запобігання якій існує індекс. І та сама перевірка майданчика, описана вище, насправді ніколи не спрацьовувала, бо не було засіяно другого майданчика, на якому вона спрацювала б.

Із роботи над центрами обробки даних у Provibr. Чому частковий унікальний індекс є єдиною справжньою межею зайнятості, є в єдиній справжній межі зайнятості стійки, а чому пʼять питань про ємність над стійкою не можна складати, є в місці у стійці, яке не є ємністю.

Читати далі

Почніть з одного хоста й одного агента

Створіть обліковий запис, встановіть агента однією командою та підключіть свій перший хост Proxmox. Пробний період триває 14 днів і не потребує платіжних даних.