martedì 29 agosto 2023

DMVCFramework Gotcha #1: Object Pool explained

Uno degli aspetti relativi alle performance di un applicativo che spesso non viene attenzionato, è l'overhead nell'esecuzione di specifici task.
L'overhead è influenzato da diversi fattori. Uno di questi, in ambito OOP,  è il tempo di creazione di un oggetto: quando creiamo un oggetto, il processo di creazione e inizializzazione può risultare più o meno lungo in funzione della complessità dell'oggetto e del processo di inizializzazione dei suoi attributi. La ripetuta creazione e distruzione di questo oggetto, influenza negativamente il tempo di runtime di tutto il singolo processo e dell'intera applicazione nel suo complesso.

Una possibile soluzione a questo problema, sarebbe quella di avere a disposizione oggetti già creati e per i quali non dobbiamo preoccuparci del loro ciclo di vita.

Uno dei Design Patterns appartenente alla categoria dei Creational Design Pattern, è l'Object Pool.
Dalla sua definizione:

l'Object Pool è un creational design pattern, ovvero uno di quei pattern applicabile per la risoluzione di problematiche di performance legate al processo di inizializzazione delle componenti (classi) di cui il nostro sistema si compone.

Il concetto alla base dell'Object Pool si può sintetizzare descrivendo un processo durante il quale un client, che necessita una istanza di una classe, chiede al Pool di fornirgliela. Il Pool, alla richiesta, 'verifica' se ha una istanza di detta classe disponibile per soddisfare la richiesta. Se l'istanza è disponibile nel pool, viene restituita; se l'istanza non è disponibile, l'Object Pool, che conosce il metodo utile alla creazione dell'istanza, ne crea una nuova e la restituisce.
Quando il client termina l'utilizzo dell'istanza ottenuta, la restituisce al Pool che non la distrugge ma la rende disponibile per le future richieste.

Questo processo è ciclico e permette, nel migliore dei casi, di abbattere l'overhead derivato dalla creazione dell'istanza quando questa è già presente nel pool.

DMVCFramework, il Framework RESTfulJSON-RPC e ActiveRecord per Delphi mantenuto da Daniele Teti e il team di DMVCFramework,  ha tante piccole, ma importanti, features forse poco conosciute. Una di questa è l'implementazione dell'ObjectPool.

La unit MVCFramework.ObjectPool.pas presente in DMVCFramework definisce una classe che implementa l'Object Pool così come il pattern lo definisce:

TObjectPool<T: class, constructor> = class(TInterfacedObject, IObjectPool<T>)

La classe (generica) TObjectPool, implementa l'interfaccia IObjectPool<T: class, constructor>
IObjectPool richiede l'implementazione di due specifici metodi per il pooling:

function GetFromPool(const RaiseExceptionIfNotAvailable: Boolean = False): T;
procedure ReleaseToPool(const Obj: T);

GetFromPool() è invocata per ottenere un oggetto dal Pool; ReleaseToPool() è invocata per rilasciare e restituire l'istanza dell'oggetto al Pool.

L'istanza della classe TObjectPool è ottenuta invocando la class function CreatePool() della classe TPoolFactory definita così:

Da notare il parametro Factory : TFunc<T> della CreatePool(): esso rappresenta il costruttore della classe che specializza il pool. Esso sarà utilizzato dal pool quando riceverà una richiesta e non vi saranno istanze disponibili.

A questo punto, mostro nel codice che segue, l'applicazione di quanto descritto con un esempio:

1) Definiamo un Pool per la classe TClientDataset:

           2) Utilizziamo l'istanza:

Con queste semplici istruzioni, abbiamo delegato al Pool la creazione e la gestione del ciclo di vita dei nostri oggetti.

A completamento di questa introduzione, pongo l'attenzione su due caratteristiche dell'Object Pool presente in DMVCFramework:
 - Il Pool ha una dimensione: POOL_SIZE indica il numero massimo di istanze mantenute nel pool (le istanze, comunque, occupano memoria!)
 - E' implementato un thread che periodicamente interviene per ripulire il pool da istanze in funzione dei valori di Shrink impostati.

Per completare, è utile sapere che esiste anche la unit MVCFramework.IntfObjectPool che in maniera analoga implementa un ObjectPool per le Interfacce.

#codinglikeacoder



giovedì 3 agosto 2023

PostgreSQL e le variabili di sessione

Nel migrare un Database Firebird ad uno PostgreSQL, mi sono trovato a dover fare il porting di stored procedure che fanno uso di variabili di contesto, definite in Firebird con le istruzioni SQL :

rdb$set_context('USER_SESSION', 'User_id', 1);
rdb$get_context('USER_SESSION', 'User_id');


Anche PostgreSQL permette di poter definire variabili legate alla sessione, con le seguenti istruzioni SQL:

set foo.username to ''flavio''  per poter definire la nuova variabile 
select current_setting(''foo.username '')  per recuperarne il valore.

Lo scope della variabile creata è limitata alla sessione: alla chiusura della sessione, la variabile non sarà più accessibile. Ed essendo una variabile il cui scope è legato alla sessione, non sarà accessibile a sessioni differenti da quella che l'ha creata.

Per fare un piccolo test, ho creato un progettino in Delphi che, eseguito due (o più volte), permette di definire una nuova variabile legata alla sessione durante la quale sono connesso al database.

Da questo link è possibile scaricare il progetto: Progetto Delphi

Dopo aver creato il vostro DB denominato mydb, mandate in esecuzione due istanze di esso contemporaneamente e provate voi stessi a verificare l'isolamento della variabile creata nell'ambito della sessione.


#codinglikeacoder




Embedding "Flavio", my name

 Hi, I'm { "embedding": [ 0.02614186331629753, 0.4381698966026306, -0.21378979086875916, 0.2108100950717926, ...