Die optimistische Nebenläufigkeitserkennung (Optimistic Concurrency Control, OCC) ist eine Strategie zur Verwaltung gemeinsam genutzter Ressourcen und zur Verhinderung von verlorenen Aktualisierungen oder Race-Bedingungen, wenn mehrere Nutzer oder Prozesse dieselbe Ressource gleichzeitig ändern.
Wenn Sie beispielsweise mit Identity and Access Management arbeiten, ist eine IAM-Richtlinie eine freigegebene Ressource, die an eine Ressource wie ein Projekt, einen Bucket oder einen Dienst angehängt ist. Ohne Nebenläufigkeitserkennung können gleichzeitige Änderungen aktuelle Richtlinienänderungen überschreiben, wodurch versehentlich legitimer Zugriff widerrufen oder nicht autorisierte Berechtigungen gewährt werden.
Zur Implementierung von OCC verwenden Systeme in der Regel eine Versionsnummer oder ein etag-Feld (Entity Tag) in der Ressourcenstruktur.
Funktionsweise von OCC
Angenommen, zwei Prozesse, A und B, versuchen, eine freigegebene Ressource gleichzeitig zu aktualisieren:
- Prozess A liest den aktuellen Status der Ressource.
- Prozess B liest denselben aktuellen Status.
- Prozess A ändert seine Kopie und schreibt sie zurück auf den Server.
- Prozess B ändert seine Kopie und schreibt sie zurück auf den Server.
Da Prozess B die Ressource überschreibt, ohne zu wissen, dass Prozess A sie bereits geändert hat, gehen die Aktualisierungen von Prozess A verloren.
OCC löst dieses Problem durch die Einführung eines eindeutigen Fingerabdrucks, der sich bei jeder Änderung einer Entität ändert. In vielen Systemen, z. B. IAM, ist dieser Fingerabdruck ein etag. Der Server prüft dieses Tag bei jedem Schreibvorgang:
- Wenn Sie die Ressource lesen, gibt der Server ein
etag(einen eindeutigen Fingerabdruck) zurück. - Wenn Sie die geänderte Ressource zurücksenden, müssen Sie die ursprüngliche
etagangeben. - Wenn die gespeicherte
etagnicht mit der von Ihnen gesendetenetagübereinstimmt, d. h. ein anderer Prozess die Ressource nach dem Lesen geändert hat, schlägt der Schreibvorgang mit einemABORTED- oderFAILED_PRECONDITION-Fehler fehl.
Dieser Fehler signalisiert dem Client, den gesamten Vorgang zu wiederholen: den aktualisierten Status noch einmal lesen, die Änderungen noch einmal anwenden und den Schreibvorgang mit dem neuen etag noch einmal versuchen.
OCC-Schleife implementieren
Der Kern einer OCC-Implementierung ist eine Wiederholungsschleife, die die Ressource liest, ändert und versucht, sie zu speichern. Nach einem erfolgreichen Schreibvorgang wird die Schleife beendet. Konfigurieren Sie eine angemessene maximale Anzahl von Wiederholungsversuchen, um Endlosschleifen bei hoher Konfliktrate zu vermeiden.
Schritte der Schleife
In der folgenden Tabelle werden die einzelnen Schritte in einer OCC-Wiederholungsschleife beschrieben:
| Schritt | Aktion | Implementierungsbeispiel |
|---|---|---|
| Lesen | Rufen Sie den aktuellen Ressourcenstatus ab, einschließlich des etag. |
let mut policy = client .get_iam_policy() .set_resource(&secret_name) .send() .await?; |
| Ändern | Wenden Sie die Änderungen auf die lokale Struktur an. | policy.bindings.push(new_binding); |
| Schreiben und prüfen | Versuchen Sie, die geänderte Ressource mit dem vorhandenen etag zu speichern, und prüfen Sie, ob Fehlercodes für Nebenläufigkeit vorliegen. |
match client .set_iam_policy() .set_resource(&secret_name) .set_policy(policy) .send() .await { Ok(p) => return Ok(p), Err(e) => { /* ... */ } } |
| Erfolg oder Wiederholung | Wenn der Schreibvorgang erfolgreich ist, wird die Schleife beendet. Wenn ein Nebenläufigkeitsfehler auftritt, erhöhen Sie den Wiederholungszähler und setzen Sie die Schleife ab dem Schritt Lesen fort. | - |
Beispiel
Im folgenden Beispiel wird gezeigt, wie Sie eine OCC-Schleife implementieren, die eine IAM-Richtlinie für eine Secret Manager-Secret-Ressource aktualisiert, indem einem Mitglied die Rolle roles/secretmanager.secretAccessor zugewiesen wird.
Wenn Sie dieses Beispiel ausführen möchten, deklarieren Sie zuerst die Abhängigkeit in Ihrer Cargo.toml-Datei:
cargo add google-cloud-secretmanager-v1
Implementieren Sie dann die OCC-Wiederholungsschleife:
Vollständiger Code
Der folgende Code zeigt die vollständige Funktion mit allen erforderlichen Importen: