perf(android): saving no longer pulls the vault it already holds
The crypto is down to about 135 ms, so the wait was never the crypto: the log says HTTP 200 in 3244 ms for 266299 bytes. Every save fetched the whole vault, decrypted it, patched it, and sent it back. The fetch existed to be sure of modifying the latest published version. The sha proves that better and for free: publish against the snapshot in hand, and if another device published in between, Gitea refuses with a 409 - then, and only then, pull for real. The retry loop was already written for exactly that. The snapshot kept is the one RECEIVED, never a re-serialisation of TVault: that would drop everything the phone does not read - attachment bodies, the avatar, tombstones - which is the invariant PMA.VaultPatch exists for. It goes at lock, with the rest of the plaintext. Note this makes the 409 path load-bearing, and it has still never run. It is now what guarantees a save cannot clobber another device. The PUT still carries the whole container; that is inherent to the format.
This commit is contained in:
+37
@@ -130,6 +130,18 @@ type
|
|||||||
/// Elle part au verrouillage, avec lui.
|
/// Elle part au verrouillage, avec lui.
|
||||||
FKey, FKeySalt: TBytes;
|
FKey, FKeySalt: TBytes;
|
||||||
FKeyIters: Integer;
|
FKeyIters: Integer;
|
||||||
|
/// L'INSTANTANE BRUT, tel qu'il a ete recu. Pas une reserialisation de
|
||||||
|
/// TVault : celle-ci perdrait tout ce que le telephone ne lit pas -
|
||||||
|
/// le contenu des pieces jointes, l'avatar, les tombstones - c'est
|
||||||
|
/// l'invariant de PMA.VaultPatch.
|
||||||
|
///
|
||||||
|
/// Il permet d'enregistrer SANS RETIRER le coffre d'abord. Le tirage
|
||||||
|
/// coutait 3,2 s pour 266 Ko, a chaque sauvegarde, alors qu'on tient deja
|
||||||
|
/// ce qu'il rapporte. Ce qu'il apportait vraiment, c'etait la certitude de
|
||||||
|
/// modifier la derniere version publiee - et le sha la donne aussi : si un
|
||||||
|
/// autre appareil a publie entre-temps, Gitea refuse en 409 et on retire
|
||||||
|
/// pour de bon.
|
||||||
|
FPlain: string;
|
||||||
/// Code d'ouverture du cache local, choisi dans l'application. JAMAIS
|
/// Code d'ouverture du cache local, choisi dans l'application. JAMAIS
|
||||||
/// ENREGISTRE : il est vide a chaque lancement et doit etre retape. C'est
|
/// ENREGISTRE : il est vide a chaque lancement et doit etre retape. C'est
|
||||||
/// ce qui le distingue du jeton, dont la persistance est un confort sans
|
/// ce qui le distingue du jeton, dont la persistance est un confort sans
|
||||||
@@ -729,6 +741,21 @@ begin
|
|||||||
begin
|
begin
|
||||||
LClient := TSyncClient.Create(FUrl, FToken);
|
LClient := TSyncClient.Create(FUrl, FToken);
|
||||||
for LAttempt := 1 to 3 do
|
for LAttempt := 1 to 3 do
|
||||||
|
begin
|
||||||
|
// PREMIER ESSAI SANS TIRER. On tient l'instantane et le sha qui va
|
||||||
|
// avec ; le tirage ne servait qu'a s'assurer de modifier la
|
||||||
|
// derniere version publiee, et le sha le prouve mieux : s'il n'est
|
||||||
|
// plus le bon, Gitea refuse. On ne retire donc que lorsqu'on a
|
||||||
|
// vraiment quelque chose a apprendre.
|
||||||
|
if (LAttempt = 1) and (FPlain <> '') and (FETag <> '') then
|
||||||
|
begin
|
||||||
|
Log('save: publishing against the snapshot in hand');
|
||||||
|
LGet.Status := ssOK;
|
||||||
|
LGet.Container := '';
|
||||||
|
LGet.ETag := FETag;
|
||||||
|
LPlain := FPlain;
|
||||||
|
end
|
||||||
|
else
|
||||||
begin
|
begin
|
||||||
Log(Format('save: attempt %d - GET', [LAttempt]));
|
Log(Format('save: attempt %d - GET', [LAttempt]));
|
||||||
// Sans empreinte connue : on VEUT le corps, pas un 304.
|
// Sans empreinte connue : on VEUT le corps, pas un 304.
|
||||||
@@ -738,6 +765,8 @@ begin
|
|||||||
LErr := Format('read failed (HTTP %d) %s', [LGet.HttpCode, LGet.Error]);
|
LErr := Format('read failed (HTTP %d) %s', [LGet.HttpCode, LGet.Error]);
|
||||||
Break;
|
Break;
|
||||||
end;
|
end;
|
||||||
|
LPlain := '';
|
||||||
|
end;
|
||||||
|
|
||||||
try
|
try
|
||||||
// UNE SEULE DERIVATION PAR ENREGISTREMENT : la variante Ex rend
|
// UNE SEULE DERIVATION PAR ENREGISTREMENT : la variante Ex rend
|
||||||
@@ -745,6 +774,8 @@ begin
|
|||||||
// reutilise. Deriver deux fois le meme mot de passe coutait une
|
// reutilise. Deriver deux fois le meme mot de passe coutait une
|
||||||
// seconde fois quatre a six secondes, pour une cle equivalente.
|
// seconde fois quatre a six secondes, pour une cle equivalente.
|
||||||
LSw := TStopwatch.StartNew;
|
LSw := TStopwatch.StartNew;
|
||||||
|
// LPlain deja pose quand on publie contre l'instantane en main.
|
||||||
|
if LPlain = '' then
|
||||||
LPlain := OpenContainer(LGet.Container, FSyncPwd);
|
LPlain := OpenContainer(LGet.Container, FSyncPwd);
|
||||||
LKey := FKey; LSalt := FKeySalt; LIter := FKeyIters;
|
LKey := FKey; LSalt := FKeySalt; LIter := FKeyIters;
|
||||||
LNew := APatch(LPlain);
|
LNew := APatch(LPlain);
|
||||||
@@ -760,7 +791,10 @@ begin
|
|||||||
LSw := TStopwatch.StartNew;
|
LSw := TStopwatch.StartNew;
|
||||||
LBody := EncryptContainerWithKey(LNew, LKey, LSalt, LIter);
|
LBody := EncryptContainerWithKey(LNew, LKey, LSalt, LIter);
|
||||||
Log(Format('save: re-encrypt in %d ms', [LSw.ElapsedMilliseconds]));
|
Log(Format('save: re-encrypt in %d ms', [LSw.ElapsedMilliseconds]));
|
||||||
|
LSw := TStopwatch.StartNew;
|
||||||
LPut := LClient.Put(LBody, LGet.ETag, AComment);
|
LPut := LClient.Put(LBody, LGet.ETag, AComment);
|
||||||
|
Log(Format('save: PUT %d chars in %d ms',
|
||||||
|
[Length(LBody), LSw.ElapsedMilliseconds]));
|
||||||
if LPut.Status = ssOK then
|
if LPut.Status = ssOK then
|
||||||
begin
|
begin
|
||||||
Log('save: published, new sha ' + LPut.ETag);
|
Log('save: published, new sha ' + LPut.ETag);
|
||||||
@@ -781,6 +815,7 @@ begin
|
|||||||
if LPut.Status = ssConflict then
|
if LPut.Status = ssConflict then
|
||||||
begin
|
begin
|
||||||
Log('save: 409 - another device published, re-reading');
|
Log('save: 409 - another device published, re-reading');
|
||||||
|
LPlain := '';
|
||||||
Continue;
|
Continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -1081,12 +1116,14 @@ begin
|
|||||||
FKey := nil;
|
FKey := nil;
|
||||||
FKeySalt := nil;
|
FKeySalt := nil;
|
||||||
FKeyIters := 0;
|
FKeyIters := 0;
|
||||||
|
FPlain := '';
|
||||||
ShowUnlock;
|
ShowUnlock;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm8.AdoptVault(const APlainJson: string);
|
procedure TForm8.AdoptVault(const APlainJson: string);
|
||||||
begin
|
begin
|
||||||
FVault.LoadFromJson(APlainJson);
|
FVault.LoadFromJson(APlainJson);
|
||||||
|
FPlain := APlainJson;
|
||||||
Log(' ' + FVault.LiveCount.ToString + ' entries, ' +
|
Log(' ' + FVault.LiveCount.ToString + ' entries, ' +
|
||||||
FVault.TrashCount.ToString + ' in trash');
|
FVault.TrashCount.ToString + ' in trash');
|
||||||
FVaultUnreadable := False;
|
FVaultUnreadable := False;
|
||||||
|
|||||||
Reference in New Issue
Block a user