Comportamento del controllo del blocco del database

Completato

Per impostazione predefinita, il runtime di Business Central determina automaticamente i livelli di isolamento usati durante l'esecuzione di query sul database. Tuttavia, gli sviluppatori AL possono controllare in modo esplicito il livello di isolamento del database sulle singole letture di un'istanza di un record.

Il livello di isolamento di una transazione determina il grado di isolamento da altre transazioni per evitare problemi in situazioni simultanee. A livello di record, il livello di isolamento migliora l'integrità e la stabilità dei dati quando più transazioni leggono lo stesso record. Protegge una transazione dagli effetti di altre transazioni accettando blocchi, impedendo la lettura di dati non sottoposti a commit o impedendo modifiche.

Il blocco del database può essere una delle principali cause di problemi di prestazioni. Quando il codice AL accetta meno blocchi, aumenta le prestazioni del sistema per gli utenti finali. Usando il livello di isolamento dell'istanza del record, è possibile migliorare le prestazioni limitando i blocchi del database al necessario.

Il runtime di Business Central determina automaticamente i livelli di isolamento usati durante l'esecuzione di query sul database. Il livello di isolamento di una transazione viene aumentato in modo implicito mediante scritture su un record o in modo esplicito tramite una chiamata al metodo LockTable, entrambi in base alle singole tabelle. Il livello di isolamento aumentato persiste per l'intera transazione, lasciando che il codice successivo eseguito ne sia influenzato, che sia necessario o voluto.

L'esempio seguente mostra il codice AL con suggerimenti sul livello di isolamento SQL annotati sulle letture del database, con un approccio basato esclusivamente sul blocco determinato dalla transazione.

```al-language

local procedure CurrentBehavior()
var
    cust: Record Customer;
    otherCust: Record Customer;
    curr: Record Currency;
begin
    cust.FindFirst(); // READUNCOMMITTED

    // Heighten isolation level for Customer table.
    cust.LockTable();
    cust.FindLast(); // UPDLOCK

    // Also impacts other instances of same table.
    otherCust.FindSet(); // UPDLOCK

    // But does not impact other tables.
    curr.Find(); // READUNCOMMITTED
end;
```

Con l'introduzione del livello di isolamento dell'istanza del record, è possibile selezionare in modo esplicito il livello di isolamento per le letture sull'istanza di un record. Il livello di isolamento dell'istanza del record sostituisce il livello di isolamento della transazione per una determinata tabella. È possibile sia aumentare che diminuire il livello di isolamento, localizzando l'effetto sull'istanza del record invece che prolungandolo per l'intera durata della transazione.

L'esempio seguente mostra il codice AL con suggerimenti sul livello di isolamento SQL annotati sulle letture del database, con il livello di isolamento dell'istanza del record usato per sostituire il livello di isolamento della transazione.

```al-language

local procedure UsingReadIsolation()
var
    cust: Record Customer;
    otherCust: Record Customer;
    curr: Record Currency;
begin
    cust.FindFirst(); // READUNCOMMITTED

    // Heighten isolation level for Customer table.
    cust.LockTable();

    // Explicitly select another isolation level than the transaction's.
    otherCust.ReadIsolation := IsolationLevel::ReadUncommitted;

    otherCust.FindSet(); // READUNCOMMITED
end;
```

L'elenco seguente descrive i diversi livelli di isolamento del tipo di opzione IsolationLevel che è possibile applicare:

  • Default

    • Segue lo stato della transazione. Equivale a non usare l'isolamento sulle letture.
  • ReadUncommitted

    • Consente letture dirty, il che significa che può leggere righe che sono state modificate da altre transazioni ma senza commit eseguito. Non accetta blocchi e ignora i blocchi di altre transazioni.
  • ReadCommitted

    • Consente letture solo su dati sottoposti a commit; in altre parole, non può leggere dati che sono stati modificati da altre transazioni e di cui non è stato ancora eseguito il commit. Tuttavia, non garantisce che le righe lette rimarranno coerenti per l'intera transazione.
  • RepeatableRead

    • Assicura che tutte le letture siano stabili mantenendo blocchi condivisi per l'intera durata della transazione. La transazione non può leggere i dati che sono stati modificati ma non ancora sottoposti a commit da altre transazioni e nessun'altra transazione può modificare i dati che sono stati letti dalla transazione corrente fino al completamento della transazione corrente.
  • UpdLock

    • Legge a scopo di aggiornamento, impedendo ad altri di leggere con lo stesso intento.

In precedenza AL forniva un controllo esplicito del livello di isolamento solo tramite il metodo LockTable, che garantiva che tutte le letture per il resto della transazione usassero UpdLock. Al contrario, con il livello di isolamento dell'istanza del record il codice può essere esplicito sulle garanzie di isolamento di cui ha bisogno e lasciare il codice successivo non influenzato dalla sua esecuzione.

L'esempio seguente aumenta il livello di isolamento sull'istanza di un record di tipo G/L Entry. Accetta il blocco sull'ultima riga, mentre le letture successive non attiveranno ulteriori blocchi da accettare. Un utilizzo di questo tipo ha senso nei casi con sottoscrittori di eventi, in cui si inserisce il codice in un flusso di logica aziendale esistente e non era previsto introdurre una chiamata LockTable che avrebbe causato il blocco delle letture successive su una tabella.

```al-language

// Gets the next "Entry No." and locks just last row.
// Without causing the rest of transaction to begin taking locks.
local procedure GetNextEntryNo(): Integer
var
    GLEntry: Record "G/L Entry";
begin
    GLEntry.ReadIsolation := IsolationLevel::UpdLock;
    GLEntry.FindLast();
    exit(GLEntry."Entry No." + 1)
end;
```

Non è possibile, all'interno di una transazione, determinare il livello di isolamento corrente usato nella transazione. Se il codice eseguito in precedenza ha attivato un livello di isolamento superiore, il conteggio sull'intera tabella necessita di blocchi sull'intera tabella. Con il livello di isolamento dell'istanza del record, ad esempio, sarebbe possibile ottenere un conteggio stimato dei record senza impedire a tutti gli altri di apportare modifiche alla tabella.

```al-language

local procedure GetEstimatedCount(tableno: Integer) : Integer
var
    rref: RecordRef;
begin
    rref.Open(tableno);
    rref.ReadIsolation := IsolationLevel::ReadUncommitted;
    exit(rref.Count);
end;
```

Quando si usano FlowFields e lo stato di transazione predefinito, è lo stato della tabella di destinazione della formula di FlowField a essere usato per determinare il livello di isolamento, non lo stato di destinazione della tabella di origine. Quando si usa il livello di isolamento dell'istanza del record, la tabella di destinazione non ha importanza, perché viene usato il livello di isolamento specificato nel metodo ReadIsolation. Si consideri l'esempio seguente.

```al-language

local procedure Foo()
var
    purchLine: Record "Purchase Line";
    curr: Record Currency;
begin
    curr.FindFirst();

    curr.CalcFields(curr."Vendor Outstanding Orders"); // READUNCOMMITED

    purchLine.LockTable();

    curr.CalcFields(curr."Vendor Outstanding Orders"); // UPDLOCK

    curr.ReadIsolation := IsolationLevel::ReadUncommitted;

    curr.CalcFields(curr."Vendor Outstanding Orders"); // READUNCOMMITTED
end;
```