Skip to content

Commit 25a547a

Browse files
committed
docs: detail inbound migration lease design
Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
1 parent 26ead79 commit 25a547a

1 file changed

Lines changed: 152 additions & 1 deletion

File tree

docs/internal/adr_inbound_migration_limit.ru.md

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,164 @@ holderIdentity: <migration-uid>
197197
- если lease существует, но владелец уже terminal или отсутствует, lease можно перехватить;
198198
- release удаляет lease или очищает `holderIdentity`, только если lease принадлежит текущей миграции.
199199

200+
### Детали реализации Lease
201+
202+
Lease должен быть отдельным служебным объектом, который представляет один inbound slot конкретной target node.
203+
204+
Рекомендуемый формат имени:
205+
206+
```text
207+
incoming-migration-<node-name-hash>
208+
```
209+
210+
Использовать только raw node name в имени нежелательно: имя node может быть длинным или содержать символы, которые потребуют нормализации. Поэтому безопаснее формировать имя из стабильного hash, а исходное имя node хранить в label или annotation.
211+
212+
Рекомендуемый объект:
213+
214+
```yaml
215+
apiVersion: coordination.k8s.io/v1
216+
kind: Lease
217+
metadata:
218+
namespace: d8-virtualization
219+
name: incoming-migration-<node-name-hash>
220+
labels:
221+
virtualization.deckhouse.io/component: inbound-migration-limiter
222+
virtualization.deckhouse.io/target-node-hash: <node-name-hash>
223+
annotations:
224+
virtualization.deckhouse.io/target-node: <target-node>
225+
virtualization.deckhouse.io/migration-namespace: <migration-namespace>
226+
virtualization.deckhouse.io/migration-name: <migration-name>
227+
virtualization.deckhouse.io/migration-uid: <migration-uid>
228+
spec:
229+
holderIdentity: <migration-namespace>/<migration-name>/<migration-uid>
230+
leaseDurationSeconds: 300
231+
acquireTime: <now>
232+
renewTime: <now>
233+
```
234+
235+
`holderIdentity` должен содержать не только UID, но и namespace/name. Это упрощает проверку владельца без list-а всех migrations во всех namespaces.
236+
237+
OwnerReference на `VirtualMachineInstanceMigration` добавлять не нужно, потому что migration namespaced, а lease хранится в namespace control plane. Cross-namespace owner reference для namespaced объектов некорректен. Очистка должна выполняться явно через `Release` и через stale lease recovery.
238+
239+
### TryAcquire
240+
241+
`TryAcquire(ctx, migration, targetNode)` должен работать так:
242+
243+
1. Построить lease name по `targetNode`.
244+
2. Выполнить `Get` lease.
245+
3. Если lease не найден:
246+
- создать lease с holder текущей migration;
247+
- если create завершился conflict/already exists, повторить `Get` и перейти к обычной проверке владельца.
248+
4. Если lease найден и принадлежит текущей migration:
249+
- обновить `renewTime`;
250+
- вернуть `true`.
251+
5. Если lease найден и принадлежит другой migration:
252+
- проверить, жива ли migration-владелец;
253+
- если владелец существует и не terminal, вернуть `false`;
254+
- если владелец отсутствует или terminal, попытаться перехватить lease через `Update` с текущим `resourceVersion`.
255+
6. Если update завершился conflict, вернуть retryable error или повторить короткий цикл reread/update.
256+
257+
Псевдокод:
258+
259+
```go
260+
func (l *LeaseIncomingMigrationLimiter) TryAcquire(ctx context.Context, mig *virtv1.VirtualMachineInstanceMigration, targetNode string) (bool, error) {
261+
lease, err := l.getLease(ctx, targetNode)
262+
if apierrors.IsNotFound(err) {
263+
return l.createLease(ctx, mig, targetNode)
264+
}
265+
if err != nil {
266+
return false, err
267+
}
268+
269+
if isHeldBy(lease, mig) {
270+
return true, l.renewLease(ctx, lease, mig)
271+
}
272+
273+
alive, err := l.holderMigrationIsActive(ctx, lease)
274+
if err != nil {
275+
return false, err
276+
}
277+
if alive {
278+
return false, nil
279+
}
280+
281+
return l.stealLease(ctx, lease, mig, targetNode)
282+
}
283+
```
284+
285+
### Проверка владельца
286+
287+
Проверка владельца lease должна использовать annotations:
288+
289+
```text
290+
virtualization.deckhouse.io/migration-namespace
291+
virtualization.deckhouse.io/migration-name
292+
virtualization.deckhouse.io/migration-uid
293+
```
294+
295+
Алгоритм:
296+
297+
1. Если annotations неполные — считать lease stale.
298+
2. Сделать `Get` `VirtualMachineInstanceMigration` по namespace/name из annotations.
299+
3. Если объект не найден — lease stale.
300+
4. Если UID объекта отличается от UID в annotation — lease stale.
301+
5. Если migration находится в terminal phase — lease stale.
302+
6. Иначе lease занят активной migration.
303+
304+
Terminal phases:
305+
306+
```text
307+
MigrationSucceeded
308+
MigrationFailed
309+
```
310+
311+
### Release
312+
313+
`Release(ctx, migration, targetNode)` должен быть идемпотентным:
314+
315+
1. Получить lease по target node.
316+
2. Если lease отсутствует — успешно завершить.
317+
3. Если lease принадлежит другой migration — ничего не делать.
318+
4. Если lease принадлежит текущей migration — удалить lease.
319+
5. Если delete получил `NotFound` — успешно завершить.
320+
321+
Удаление lease предпочтительнее очистки `holderIdentity`, потому что отсутствие lease проще обрабатывать в `TryAcquire`, а stale пустые lease не будут накапливаться.
322+
323+
### Renew
324+
325+
Так как lease используется не для leader election, а как атомарный slot, постоянный renew не обязателен. Достаточно обновлять `renewTime` при каждом reconcile migration, которая уже владеет lease.
326+
327+
`leaseDurationSeconds` нужен только как дополнительная диагностическая и safety-информация. Нельзя освобождать lease только по истечению времени, если migration-владелец всё ещё существует и не terminal: долгие live migrations допустимы.
328+
329+
### Требования к client/cache
330+
331+
Операции `Get/Create/Update/Delete` для Lease желательно выполнять через non-cached client или APIReader, если это доступно в месте интеграции. Это снижает риск решений на устаревшем cache.
332+
333+
Даже при cached read корректность должна обеспечиваться optimistic concurrency Kubernetes API:
334+
335+
- создать lease сможет только одна migration;
336+
- перехват stale lease выполняется через `resourceVersion`;
337+
- conflict приводит к повторному reconcile.
338+
339+
### RBAC
340+
341+
`virt-controller` должен получить права на leases в namespace `d8-virtualization`:
342+
343+
```text
344+
apiGroups: ["coordination.k8s.io"]
345+
resources: ["leases"]
346+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
347+
```
348+
349+
`list/watch` нужны только если реализация использует informer/cache или периодический cleanup. Для минимальной реализации достаточно `get/create/update/delete`, но в controller-runtime окружении часто проще выдать полный набор read/write verbs для leases.
350+
200351
### Обработка stale lease
201352

202353
Lease может остаться после аварийного завершения controller-а или удаления migration resource.
203354

204355
При обнаружении занятого lease controller должен проверить владельца:
205356

206-
1. найти `VirtualMachineInstanceMigration` по UID владельца;
357+
1. найти `VirtualMachineInstanceMigration` по namespace/name и сверить UID владельца;
207358
2. если владелец отсутствует или terminal, считать lease stale;
208359
3. перехватить lease через optimistic update с `resourceVersion`.
209360

0 commit comments

Comments
 (0)