Блог ·
Єдина справжня межа зайнятості стійки
Двоє інженерів водночас обирають той самий юніт стійки. Форма каже обом, що він вільний. Вирішує частковий унікальний індекс, і ніщо інше не може.
Автор: Provibr
Двоє інженерів розміщують машини в одній стійці. Обидва відкривають форму, обидва дивляться на U9, обидва бачать вільний слот, обидва тиснуть «зберегти». Хоч би що робив ваш код застосунку між тими двома запитами, є вікно, у якому обидва читають ту саму відповідь, і жодна кількість перевірок перед записом його не закриває.
Це найстаріші перегони з підручника, і в них є одна правильна відповідь: вирішує база даних. Усе над нею існує, щоб дати придатну для читання відмову, а не щоб запобігти зіткненню. Переставити цей порядок місцями є рівно способом отримати правило унікальності, що протікає в заклопотаний день, а в центрі обробки даних протікання означає когось із візком, хто стоїть перед уже повною стійкою.
Межею є один індекс
Зайнятість зберігається двічі навмисно. Заявка має рядок заголовка, і це та річ, яку хтось зробив: цей бік, від цього юніта до того, а для розміщення ще й машина, яка в ньому стоїть. І вона має по рядку на кожен зайнятий слот: ця стійка, цей бік, цей один U.
Таблиця слотів існує рівно з однієї причини. Вона несе частковий унікальний індекс на стійку, бік і номер слота, обмежений рядками, які не звільнено. Той індекс є єдиною справжньою межею зайнятості в усій системі. Ніщо інше нею бути не може, бо унікальний індекс не вміє зʼєднувати: він бачить лише колонки того рядка, на якому сидить, тож стійку й бік доводиться денормалізувати в рядок слота, щоб обмеження взагалі існувало.
Ціною є те, що звільнення заявки це два оновлення всередині однієї транзакції, бо позначка часу звільнення живе і в заголовку, і в слотах. Віддачею є те, що інваріант забезпечує база даних, а не дбайливість того, хто пише наступну можливість.
Ми це виміряли, а не вивели міркуванням. Два справжні зʼєднання до Postgres, взаємний барʼєр, щоб обидва були всередині своєї транзакції перш ніж хоч одне зафіксується, обидва заявляють передній U9. Результат: записано рівно одну заявку, одна помилка 23505 із назвою того індексу, і жодного осиротілого заголовка, лишеного відкотом. Це те речення, яке ви хочете могти написати про гарантію паралельності, а з читання коду ви його не напишете.
Для чого насправді потрібна перевірка перед записом
Є чиста функція, яка планує заявку, і вона відмовляє трьома способами, у такому порядку.
Бік не існує. Відкрита рама без задньої частини не є стійкою, у задній бік якої щось вішають, і саме тому те, чи підтримує стійка передній і задній бік, є властивістю стійки, а не припущенням.
Поза межами. Увесь діапазон мусить уміститися між початковим юнітом стійки та її кінцем, включно. Не всі стійки починаються з одиниці й не всі нумеруються вгору, тож це арифметика проти геометрії саме тієї стійки, а не проти числа сорок два.
Зайнято, і ця відмова несе перелік слотів, які конфліктують. Повідомлення, яке каже «повно», відправляє людину назад до форми без уявлення, куди дивитися. Повідомлення, яке каже, що передні U9 та U10 зайняті, відправляє її до відповіді.
Ніщо з цього не є межею. Обидва інженери з першого абзацу проходять усі три. Функція існує, щоб звичайний випадок, коли одна людина за раз помиляється, давав речення, а не код помилки бази даних.
Невідома глибина поводиться як повна
Кожен актив має клас глибини: повна, половинна або невідома. Річ половинної глибини заявляє лише той бік, на якому висить. Річ повної глибини заявляє передній і задній.
Невідома заявляє передній і задній так само, і цей вибір варто відстояти, бо інтуїтивним типовим є протилежне. Невідоме відчувається як дані, яких бракує і які ви заповните потім, а поки чекаєте, лишити другий бік вільним видається нешкідливим.
Це не нешкідливо. Машину, чия глибина виявиться повною, не встановити, якщо тим часом хтось заповнив задній бік, а виправлення стається, коли машина вже на місці. Блокування заднього боку коштує вам слота, який ви могли б мати. Незаблокування коштує вам доставки. Невідоме є не відсутністю обмеження, а обмеженням, яке ви ще не можете назвати, і безпечним його боком є консервативний.
Той самий принцип проходить крізь решту моделі ємності: невідома вага, невідоме споживання потужності чи застарілий стан заявки читаються як невідоме або заблоковане, і ніколи як вільна ємність.
Розміщення ніколи не спливає
Заявки бувають чотирьох видів. Розміщення це машина, яка фізично там. Блокування це місце, виведене з експлуатації задля кабелів. Утримання на час переміщення і резервування це ті два, що тримають місце для чогось у русі або в замовленні. Екран сьогодні має лише перше; інші три існують як дані, щоб примітив був однією річчю, а не чотирма схожими.
Обмеження в базі даних забезпечує, що розміщення не має строку дії. Це звучить як маленьке правило. Воно запобігає рядку, який тихо перестає рахуватися у вівторок, поки машина досі прикручена, тобто стійці, у якій програма вважає вільним слот. Резервування, що спливає, є можливістю; розміщення, що спливає, є брехнею з таймером.
Друге обмеження вимагає, щоб розміщення мало актив, бо інші три види законно не мають жодного, а частковий унікальний індекс дозволяє щонайбільше одне активне розміщення на актив. Без того індексу ту саму машину можна записати у дві стійки водночас. Це не питання якості даних, яке ви помічаєте у звіті; це питання, яке ви помічаєте, коли хтось стоїть біля не тієї стійки.
Про індекси такої форми варто знати: вони є глобальними, а не обмеженими на кожну організацію. Машина, яка вже несе активний рядок, стикається з унікальним обмеженням, а не з перевіркою володіння, навіть коли друга спроба надходить від іншої організації. Це безпечний бік, і це також означає, що для випробування випадку з двома організаціями потрібен третій актив, бо обмеження спрацьовує перш ніж узагалі виконається логіка володіння.
Та сама форма на рівень вище
Передача машини клієнтові має тотожну будову, і їй потрібні два індекси, а не один, бо це два інваріанти, які люди звично проговорюють одним реченням.
Одна послуга є щонайбільше на одній машині. Без цього послуга може вказувати на два активи, а її картка розташування вказує у два боки.
Одна машина видана щонайбільше одній послузі. Без цього ту саму машину продають двічі, і ви дізнаєтеся про це, коли зателефонують двоє клієнтів.
Обидва є частковими унікальними індексами на рядках, чия дата завершення досі порожня, а це та сама конструкція, що й індекс слотів, і з тієї самої причини: перевірка в застосунку дає придатну для читання відмову, індекс вирішує, хто переміг.
Одне правило не змогло потрапити в базу даних. Привʼязка мусить вказувати на виділену послугу, а обмеження CHECK не може читати іншу таблицю. Це живе у функції, що пише привʼязку, і варто прямо казати, які з ваших правил забезпечує схема, а які забезпечує шлях у коді, бо другий вид вартий рівно стільки, скільки шляхів запису ви не забули через нього провести.
Привʼязки є історичними, а не поточними. Дата завершення це те, що робить рядок неактивним, і кожне читання фізичного розташування фільтрує за нею та за організацією і привʼязки, і актива. Приберіть ті фільтри, і колишній клієнт прочитає стійку та юніт клієнта, який прийшов після нього. Це доведено червоним перш ніж було виправлено, а це єдиний спосіб знати, що фільтр робить роботу.
Що ми б записали
- Кладіть унікальність туди, де рядок. Якщо обмеженню потрібне зʼєднання, денормалізуйте потрібні йому колонки; індекс не вміє зʼєднувати, а тригер не є обмеженням.
- Матеріалізуйте одиницю суперечки. По рядку на кожен зайнятий слот, а не діапазон у заголовку, бо діапазон не може бути унікальним проти іншого діапазону.
- Доведіть перегони двома зʼєднаннями й барʼєром. Міркування про рівні ізоляції не є доказом.
- Хай попередня перевірка буде повідомленням, а не захистом, і хай вона називає точні слоти, які конфліктують.
- Зробіть так, щоб невідоме поводилося як дорогий випадок, аби відсутнє вимірювання ніколи не подавалося як вільне місце.
Заради ясності, де це стоїть: робота над логістикою центру обробки даних лежить за прапорцем розгортання, який типово вимкнено, а поверх нього налаштування на кожну організацію, тож ніщо з наведеного сьогодні не купити. Дві речі недоведені: вимірювання паралельності проведено на заявках блокування у прямому випробуванні бази даних, а не на самому шляху розміщення, а види «утримання на час переміщення» та «резервування» досі не мають того, хто їх пише. Ширша звичка дозволяти базі даних бути арбітром зʼявляється в цьому продукті й в іншому: у тому, як адреси видають із пулу і в тому, як діапазон vmid насправді є межею дозволу.
Із роботи над центрами обробки даних у Provibr. Чому пʼять питань про ємність над стійкою не можна складати, є в місці у стійці, яке не є ємністю, а те, що стається, коли зовнішня система стверджує, що знає, де стоїть машина, описано в коли NetBox є джерелом правди.