{"elements":[{"id":7008,"title":"Sistema Cache","body":"<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Caching trasparente<\/h2>\n    <p class=\"lead\">DressAPI include un livello di caching trasparente tra la logica applicativa e il database. Quando abilitato, ogni lettura (SELECT) viene automaticamente cachata e ogni operazione di scrittura (INSERT, UPDATE, DELETE) invalida automaticamente le voci di cache pertinenti, incluse quelle delle tabelle correlate.<\/p>\n\n    <h3 class=\"mt-4\">Principi di progettazione<\/h3>\n    <ul>\n      <li><strong>Zero codice applicativo richiesto<\/strong> &ndash; il caching &egrave; gestito a livello framework tramite i metodi <code>CDB<\/code><\/li>\n      <li><strong>Due backend intercambiabili<\/strong> &ndash; basato su file (filesystem RAM <code>\/dev\/shm\/<\/code>) o Redis<\/li>\n      <li><strong>Isolamento per utente<\/strong> &ndash; le voci di cache possono essere associate a un utente o globali<\/li>\n      <li><strong>Invalidazione automatica<\/strong> &ndash; le operazioni di scrittura cancellano la cache per la tabella interessata e tutte le tabelle collegate via chiavi esterne<\/li>\n      <li><strong>Crittografia opzionale<\/strong> &ndash; i dati in cache possono essere cifrati a riposo con AES-256-CBC<\/li>\n    <\/ul>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Architettura<\/h2>\n    <p>Il sistema cache segue il pattern classico interfaccia\/factory\/implementazione:<\/p>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Struttura delle classi<\/div>\n<span class=\"fn\">ICache<\/span> (interfaccia)\n  ├── <span class=\"fn\">CFileCache<\/span>   (implementazione filesystem)\n  └── <span class=\"fn\">CRedisCache<\/span>  (implementazione Redis)\n\n<span class=\"fn\">CCache<\/span> (factory)\n  └── <span class=\"fn\">CreateInstance<\/span>() &rarr; restituisce <span class=\"fn\">ICache<\/span>|<span class=\"kw\">null<\/span> in base alla configurazione\n    <\/div>\n\n    <h3 class=\"mt-4\">Metodi dell&#39;interfaccia ICache<\/h3>\n    <p>L&#39;interfaccia <code>ICache<\/code> definisce metodi accoppiati per operazioni specifiche per utente e globali:<\/p>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Per utente<\/th><th>Globale<\/th><th>Descrizione<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>Set()<\/code><\/td><td><code>SetGlobal()<\/code><\/td><td>Memorizza un valore<\/td><\/tr>\n          <tr><td><code>Get()<\/code><\/td><td><code>GetGlobal()<\/code><\/td><td>Recupera un valore<\/td><\/tr>\n          <tr><td><code>Exists()<\/code><\/td><td><code>ExistsGlobal()<\/code><\/td><td>Verifica se la chiave esiste<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n    <p>Il metodo <code>ClearArea()<\/code> cancella tutte le voci di cache per una data area (nome tabella). <code>Delete()<\/code> rimuove un singolo elemento.<\/p>\n\n    <h3 class=\"mt-4\">File chiave<\/h3>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>File<\/th><th>Scopo<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>core\/src\/cache\/ICache.php<\/code><\/td><td>Interfaccia che definisce tutte le operazioni di cache<\/td><\/tr>\n          <tr><td><code>core\/src\/cache\/CCache.php<\/code><\/td><td>Classe factory che istanzia il driver corretto<\/td><\/tr>\n          <tr><td><code>core\/src\/cache\/CFileCache.php<\/code><\/td><td>Implementazione cache su filesystem<\/td><\/tr>\n          <tr><td><code>core\/src\/cache\/CRedisCache.php<\/code><\/td><td>Implementazione cache Redis (usa <code>predis\/predis<\/code>)<\/td><\/tr>\n          <tr><td><code>core\/src\/cache\/config-cache.php<\/code><\/td><td>Configurazione generale (switch principale, selezione driver)<\/td><\/tr>\n          <tr><td><code>core\/src\/cache\/config-file-cache.php<\/code><\/td><td>Impostazioni cache su file (percorso, crittografia)<\/td><\/tr>\n          <tr><td><code>core\/src\/cache\/config-redis-cache.php<\/code><\/td><td>Impostazioni connessione Redis<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Implementazione cache su file (CFileCache)<\/h2>\n    <p>Memorizza ogni elemento come file serializzato (e opzionalmente cifrato) sul filesystem. Il percorso predefinito <code>\/dev\/shm\/<\/code> &egrave; un filesystem basato su RAM su Linux, fornendo velocit&agrave; di lettura prossime a zero latenza.<\/p>\n\n    <h3 class=\"mt-4\">Struttura del percorso di archiviazione<\/h3>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Formato percorso<\/div>\n{CACHE_PATH}{DOMAIN_NAME}\/cache\/{APP_NAME}\/{area_name}\/{user_part}.{safe_name}.cache\n\n<span class=\"cm\">\/\/ Esempio concreto:<\/span>\n\/dev\/shm\/dressapi.com\/cache\/core\/blog_post\/user_5.a3f8c1d9e2b4.cache\n    <\/div>\n\n    <h3 class=\"mt-4\">Vantaggi<\/h3>\n    <ul>\n      <li>Nessuna dipendenza esterna<\/li>\n      <li>Alte prestazioni quando si usa <code>\/dev\/shm\/<\/code> (tmpfs basato su RAM)<\/li>\n      <li>Semplice da ispezionare e debuggare (file su disco)<\/li>\n    <\/ul>\n    <h3 class=\"mt-4\">Considerazioni<\/h3>\n    <ul>\n      <li>I file persistono solo finch&eacute; il filesystem esiste (<code>\/dev\/shm\/<\/code> viene svuotato al riavvio)<\/li>\n      <li>Non condiviso tra pi&ugrave; server a meno che non si usi un filesystem condiviso<\/li>\n    <\/ul>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Implementazione cache Redis (CRedisCache)<\/h2>\n    <p>Memorizza ogni elemento come coppia chiave-valore Redis. Utilizza la libreria PHP <code>predis\/predis<\/code>.<\/p>\n\n    <h3 class=\"mt-4\">Installazione<\/h3>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Terminale<\/div>\n<span class=\"kw\">cd<\/span> core\/\ncomposer <span class=\"kw\">require<\/span> predis\/predis\n    <\/div>\n\n    <h3 class=\"mt-4\">Struttura della chiave<\/h3>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Formato chiave Redis<\/div>\n{DOMAIN_NAME}:{APP_NAME}:{area_name}:{user_part}:{safe_name}\n\n<span class=\"cm\">\/\/ Esempio concreto:<\/span>\ndressapi.com:core:blog_post:user_5:a3f8c1d9e2b4\n    <\/div>\n\n    <h3 class=\"mt-4\">Vantaggi<\/h3>\n    <ul>\n      <li>Condiviso tra pi&ugrave; server applicativi<\/li>\n      <li>Supporta TTL nativo (time-to-live)<\/li>\n      <li>Usa Redis <code>SCAN<\/code> per iterazione sicura e non bloccante durante la cancellazione della cache<\/li>\n    <\/ul>\n    <h3 class=\"mt-4\">Considerazioni<\/h3>\n    <ul>\n      <li>Richiede un server Redis in esecuzione e la libreria <code>predis\/predis<\/code><\/li>\n      <li>Se Redis &egrave; irraggiungibile alla costruzione, il client viene impostato a <code>null<\/code> e tutte le operazioni degradano silenziosamente a no-op<\/li>\n    <\/ul>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Struttura della chiave di cache<\/h2>\n    <p>Le chiavi di cache sono costruite da pi&ugrave; componenti per garantire l&#39;isolamento tra domini, applicazioni, moduli e utenti.<\/p>\n\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Componente<\/th><th>Sorgente<\/th><th>Scopo<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><strong>Dominio<\/strong><\/td><td>Costante <code>DOMAIN_NAME<\/code><\/td><td>Isola la cache tra diversi siti\/domini<\/td><\/tr>\n          <tr><td><strong>Applicazione<\/strong><\/td><td>Costante <code>APP_NAME<\/code><\/td><td>Isola la cache tra diverse app sullo stesso dominio<\/td><\/tr>\n          <tr><td><strong>Area<\/strong><\/td><td>Nome tabella o gruppo logico<\/td><td>Raggruppa le voci correlate per invalidazione massiva<\/td><\/tr>\n          <tr><td><strong>Parte utente<\/strong><\/td><td><code>global<\/code> o <code>user_{id}<\/code><\/td><td>Separa i dati per utente dai dati condivisi<\/td><\/tr>\n          <tr><td><strong>Nome<\/strong><\/td><td>Hash della query (tramite <code>DB_ENC_ALGORITHM<\/code>)<\/td><td>Identifica univocamente il dato specifico<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n\n    <h3 class=\"mt-4\">Generazione delle chiavi in CDB<\/h3>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">CDB::GetCacheKey()<\/div>\n<span class=\"cm\">\/\/ La chiave viene generata tramite hash<\/span>\n<span class=\"kw\">$cache_key<\/span> = <span class=\"fn\">hash<\/span>(<span class=\"fn\">DB_ENC_ALGORITHM<\/span>, <span class=\"kw\">$key<\/span>);\n<span class=\"cm\">\/\/ dove $key &egrave; tipicamente: &quot;{sql_query}.{method_name}.{parameters}&quot;<\/span>\n\n<span class=\"cm\">\/\/ Esempio per GetCachedFetchAssoc():<\/span>\n<span class=\"kw\">$cache_key<\/span> = <span class=\"fn\">GetCacheKey<\/span>(<span class=\"kw\">$sql<\/span> . <span class=\"str\">&#39;.getCachedFetchAssoc.&#39;<\/span> . <span class=\"kw\">$pos_row<\/span> . <span class=\"str\">&#39;.&#39;<\/span> . <span class=\"fn\">implode<\/span>(<span class=\"str\">&#39;.&#39;<\/span>, <span class=\"kw\">$bind_param_values<\/span>));\n    <\/div>\n    <p>Questo garantisce che query diverse, offset di paginazione diversi e valori dei parametri diversi producano chiavi di cache distinte.<\/p>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Scope per utente vs globali<\/h2>\n    <p>Gli elementi <strong>globali<\/strong> (memorizzati con <code>uid = 0<\/code>) sono condivisi tra tutti gli utenti. Questo viene usato per dati che non variano per utente:<\/p>\n    <ul>\n      <li>Strutture delle tabelle del database (area <code>structures<\/code>)<\/li>\n      <li>Definizioni dei moduli<\/li>\n      <li>Stringhe di traduzione<\/li>\n      <li>Mappature delle rotte<\/li>\n    <\/ul>\n    <p>Gli elementi <strong>per utente<\/strong> sono limitati all&#39;ID dell&#39;utente corrente, garantendo che i risultati delle query influenzati da ACL o filtri <code>only_owner<\/code> rimangano correttamente isolati.<\/p>\n\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Utilizzo scope<\/div>\n<span class=\"cm\">\/\/ Cache globale (condivisa tra tutti gli utenti)<\/span>\n<span class=\"kw\">$this<\/span>-&gt;<span class=\"fn\">GetCachedData<\/span>(<span class=\"kw\">$cache_key<\/span>, <span class=\"str\">&#39;structures&#39;<\/span>, <span class=\"fn\">true<\/span>);\n<span class=\"kw\">$this<\/span>-&gt;<span class=\"fn\">SetCachedData<\/span>(<span class=\"kw\">$cache_key<\/span>, <span class=\"kw\">$data<\/span>, <span class=\"str\">&#39;structures&#39;<\/span>, <span class=\"fn\">true<\/span>);\n\n<span class=\"cm\">\/\/ Cache per utente (predefinita)<\/span>\n<span class=\"kw\">$this<\/span>-&gt;<span class=\"fn\">GetCachedData<\/span>(<span class=\"kw\">$cache_key<\/span>, <span class=\"kw\">$this<\/span>-&gt;table);\n<span class=\"kw\">$this<\/span>-&gt;<span class=\"fn\">SetCachedData<\/span>(<span class=\"kw\">$cache_key<\/span>, <span class=\"kw\">$data<\/span>, <span class=\"kw\">$this<\/span>-&gt;table);\n    <\/div>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Caching automatico delle GET<\/h2>\n    <p>Tutti i metodi <code>GetCached*<\/code> in <code>CDB<\/code> seguono lo stesso pattern:<\/p>\n    <ol>\n      <li>Costruisci la chiave di cache da SQL + parametri<\/li>\n      <li>Tenta di leggere dalla cache<\/li>\n      <li>Se <strong>HIT<\/strong> &rarr; restituisci i dati dalla cache (nessuna query al DB)<\/li>\n      <li>Se <strong>MISS<\/strong> &rarr; esegui la query, memorizza il risultato nella cache, restituisci i dati<\/li>\n    <\/ol>\n\n    <h3 class=\"mt-4\">Metodi cached disponibili in CDB<\/h3>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Metodo<\/th><th>Cosa cacha<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>GetCachedFetchAssoc()<\/code><\/td><td>Singola riga come array associativo<\/td><\/tr>\n          <tr><td><code>GetCachedFetchRow()<\/code><\/td><td>Singola riga come array numerico<\/td><\/tr>\n          <tr><td><code>GetCachedDataValue()<\/code><\/td><td>Singolo valore scalare<\/td><\/tr>\n          <tr><td><code>GetCachedDataTable()<\/code><\/td><td>Intero result set come array<\/td><\/tr>\n          <tr><td><code>GetCachedArrayByName()<\/code><\/td><td>Coppie chiave-valore dal result set<\/td><\/tr>\n          <tr><td><code>GetCachedIDsArray()<\/code><\/td><td>Singola colonna come array<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n    <p>Ciascuno ha una controparte non cached (<code>GetQueryFetchAssoc()<\/code>, <code>GetQueryDataTable()<\/code>, ecc.) che accede sempre al database.<\/p>\n\n    <h3 class=\"mt-4\">Ciclo di vita della richiesta con cache<\/h3>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Flusso richiesta<\/div>\nRichiesta Client (GET \/blog_post)\n  &darr;\nbootstrap.php\n  ├─ CCache::CreateInstance()  &rarr; ICache o null\n  ├─ CRequest::CreateInstance($connection_manager, $cache, ...)\n  ├─ CUser::CreateInstance($request, $connection_manager, $cache, ...)\n  └─ CTranslator::Init($connection_manager, $cache, ...)\n  &darr;\nCBaseController::Exec()\n  └─ ExecGET()\n      └─ CBaseModel::GetData()\n          └─ CDB::GetCachedDataTable()  &larr; ricerca cache qui\n              ├─ cache HIT? restituisci dati\n              └─ cache MISS? query DB, memorizza, restituisci\n    <\/div>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Invalidazione della cache<\/h2>\n    <p>Ogni operazione di scrittura in <code>CDB<\/code> (<code>InsertRecord<\/code>, <code>UpdateRecord<\/code>, <code>DeleteRecord<\/code>) chiama <code>InvalidateRelatedCache()<\/code> in caso di successo.<\/p>\n\n    <h3 class=\"mt-4\">Invalidazione delle tabelle correlate<\/h3>\n    <p><code>InvalidateRelatedCache()<\/code> non cancella solo la cache per la tabella modificata &ndash; cancella anche la cache per ogni tabella che ha una chiave esterna che punta alla tabella modificata.<\/p>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">InvalidateRelatedCache()<\/div>\n<span class=\"kw\">protected function<\/span> <span class=\"fn\">InvalidateRelatedCache<\/span>(?<span class=\"kw\">string<\/span> <span class=\"kw\">$table<\/span>): <span class=\"kw\">void<\/span>\n{\n    <span class=\"kw\">if<\/span> (<span class=\"kw\">$this<\/span>-&gt;cache)\n    {\n        <span class=\"kw\">$table<\/span> ??= <span class=\"kw\">$this<\/span>-&gt;table;\n        <span class=\"kw\">$this<\/span>-&gt;cache-&gt;<span class=\"fn\">ClearArea<\/span>(<span class=\"kw\">$table<\/span>);\n\n        <span class=\"cm\">\/\/ Trova tutte le tabelle che referenziano questa via chiave esterna<\/span>\n        <span class=\"kw\">$related_fieldname<\/span> = <span class=\"fn\">str_replace<\/span>(<span class=\"str\">&#39;[related_table]&#39;<\/span>, <span class=\"kw\">$table<\/span>, <span class=\"fn\">RELATED_TABLE_ID<\/span>);\n        <span class=\"kw\">$all_dbtables<\/span> = <span class=\"kw\">$this<\/span>-&gt;<span class=\"fn\">GetAllTables<\/span>();\n\n        <span class=\"kw\">foreach<\/span> (<span class=\"kw\">$all_dbtables<\/span> <span class=\"kw\">as<\/span> <span class=\"kw\">$table_rel<\/span> =&gt; <span class=\"kw\">$fields<\/span>)\n            <span class=\"kw\">if<\/span> (<span class=\"fn\">isset<\/span>(<span class=\"kw\">$fields<\/span>[<span class=\"kw\">$related_fieldname<\/span>]))\n                <span class=\"kw\">$this<\/span>-&gt;cache-&gt;<span class=\"fn\">ClearArea<\/span>(<span class=\"kw\">$table_rel<\/span>);\n    }\n}\n    <\/div>\n    <p>Ad esempio, se si inserisce nella tabella <code>category<\/code>: (1) tutta la cache nell&#39;area <code>category<\/code> viene cancellata, (2) ogni tabella con una colonna <code>id_category<\/code> ha la sua area cancellata.<\/p>\n\n    <h3 class=\"mt-4\">Invalidazione manuale<\/h3>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">Cancellazione manuale<\/div>\n<span class=\"cm\">\/\/ Cancella un&#39;intera area (es. dopo un import massivo)<\/span>\n<span class=\"kw\">$cache<\/span>-&gt;<span class=\"fn\">ClearArea<\/span>(<span class=\"str\">&#39;blog_post&#39;<\/span>);\n\n<span class=\"cm\">\/\/ Elimina un singolo elemento<\/span>\n<span class=\"kw\">$cache<\/span>-&gt;<span class=\"fn\">Delete<\/span>(<span class=\"str\">&#39;specific_key&#39;<\/span>);\n    <\/div>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Crittografia a riposo<\/h2>\n    <p>Sia <code>CFileCache<\/code> che <code>CRedisCache<\/code> supportano la crittografia opzionale dei dati tramite OpenSSL. Il processo &egrave;:<\/p>\n    <ol>\n      <li>I dati vengono serializzati con <code>serialize()<\/code> di PHP<\/li>\n      <li>Se la crittografia &egrave; abilitata, la stringa viene cifrata con <code>openssl_encrypt()<\/code> usando un IV casuale<\/li>\n      <li>L&#39;IV viene anteposto al testo cifrato e il risultato viene codificato in base64<\/li>\n      <li>In lettura il processo &egrave; inverso: decodifica base64, estrai IV, decifra, deserializza<\/li>\n    <\/ol>\n\n    <h3 class=\"mt-4\">Costanti di crittografia<\/h3>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Costante<\/th><th>Predefinito<\/th><th>Descrizione<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>CACHE_ENCRYPTION_ENABLED<\/code><\/td><td><code>true<\/code><\/td><td>Abilita\/disabilita la crittografia dei dati in cache<\/td><\/tr>\n          <tr><td><code>CACHE_ENCRYPTION_KEY<\/code><\/td><td><code>&#39;change-this-...&#39;<\/code><\/td><td>Chiave di crittografia AES. <strong>Deve essere cambiata in produzione!<\/strong><\/td><\/tr>\n          <tr><td><code>CACHE_ENCRYPTION_CIPHER<\/code><\/td><td><code>&#39;aes-256-cbc&#39;<\/code><\/td><td>Algoritmo di cifratura OpenSSL<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Tutte le costanti di configurazione<\/h2>\n\n    <h3 class=\"mt-4\">Configurazione generale<\/h3>\n    <p>Definite in <code>core\/src\/cache\/config-cache.php<\/code>:<\/p>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Costante<\/th><th>Predefinito<\/th><th>Descrizione<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>CACHE_ENABLED<\/code><\/td><td><code>false<\/code><\/td><td>Switch principale. Deve essere <code>true<\/code> perch&eacute; il caching funzioni<\/td><\/tr>\n          <tr><td><code>CACHE_DRIVER<\/code><\/td><td><code>&#39;file&#39;<\/code><\/td><td>Driver backend: <code>&#39;file&#39;<\/code> o <code>&#39;redis&#39;<\/code><\/td><\/tr>\n          <tr><td><code>CACHE_MAX_SIZE_PER_ELEMENT<\/code><\/td><td><code>65536<\/code> (64 KB)<\/td><td>Dimensione massima in byte per elemento. Gli elementi pi&ugrave; grandi vengono ignorati<\/td><\/tr>\n          <tr><td><code>DOMAIN_NAME<\/code><\/td><td><code>&#39;dressapi&#39;<\/code><\/td><td>Prefisso\/namespace per isolamento tra domini<\/td><\/tr>\n          <tr><td><code>APP_NAME<\/code><\/td><td><code>&#39;core&#39;<\/code><\/td><td>Prefisso\/namespace per isolamento tra applicazioni<\/td><\/tr>\n          <tr><td><code>DB_ENC_ALGORITHM<\/code><\/td><td><code>&#39;tiger192,4&#39;<\/code><\/td><td>Algoritmo hash per generare le chiavi di cache dalle query SQL<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n\n    <h3 class=\"mt-4\">Impostazioni cache su file<\/h3>\n    <p>Definite in <code>core\/src\/cache\/config-file-cache.php<\/code>:<\/p>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Costante<\/th><th>Predefinito<\/th><th>Descrizione<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>CACHE_PATH<\/code><\/td><td><code>&#39;\/dev\/shm\/&#39;<\/code><\/td><td>Directory base per i file di cache. <code>\/dev\/shm\/<\/code> &egrave; un filesystem RAM per prestazioni massime<\/td><\/tr>\n          <tr><td><code>CACHE_ENCRYPTION_ENABLED<\/code><\/td><td><code>true<\/code><\/td><td>Crittografia dei dati a riposo<\/td><\/tr>\n          <tr><td><code>CACHE_ENCRYPTION_KEY<\/code><\/td><td><code>&#39;change-this-...&#39;<\/code><\/td><td>Chiave crittografia AES<\/td><\/tr>\n          <tr><td><code>CACHE_ENCRYPTION_CIPHER<\/code><\/td><td><code>&#39;aes-256-cbc&#39;<\/code><\/td><td>Algoritmo OpenSSL<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n\n    <h3 class=\"mt-4\">Impostazioni Redis<\/h3>\n    <p>Definite in <code>core\/src\/cache\/config-redis-cache.php<\/code>:<\/p>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Costante<\/th><th>Predefinito<\/th><th>Descrizione<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>REDIS_SCHEME<\/code><\/td><td><code>&#39;tcp&#39;<\/code><\/td><td>Schema di connessione<\/td><\/tr>\n          <tr><td><code>REDIS_HOST<\/code><\/td><td><code>&#39;127.0.0.1&#39;<\/code><\/td><td>Hostname del server Redis<\/td><\/tr>\n          <tr><td><code>REDIS_PORT<\/code><\/td><td><code>6379<\/code><\/td><td>Porta del server Redis<\/td><\/tr>\n          <tr><td><code>REDIS_USERNAME<\/code><\/td><td><code>null<\/code><\/td><td>Username Redis opzionale (ACL Redis 6+)<\/td><\/tr>\n          <tr><td><code>REDIS_PASSWORD<\/code><\/td><td><code>null<\/code><\/td><td>Password Redis opzionale<\/td><\/tr>\n          <tr><td><code>REDIS_DATABASE<\/code><\/td><td><code>0<\/code><\/td><td>Indice database Redis (0-15)<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Punti di integrazione nel bootstrap<\/h2>\n    <p>L&#39;istanza di cache viene creata una volta in <code>bootstrap.php<\/code> e iniettata in ogni componente principale:<\/p>\n    <div class=\"da-snippet\">\n      <div class=\"da-snippet-label\">bootstrap.php<\/div>\n<span class=\"kw\">$cache<\/span>      = <span class=\"fn\">CCache<\/span>::<span class=\"fn\">CreateInstance<\/span>();\n<span class=\"kw\">$request<\/span>    = <span class=\"fn\">CRequest<\/span>::<span class=\"fn\">CreateInstance<\/span>(<span class=\"kw\">$connection_manager<\/span>, <span class=\"kw\">$cache<\/span>, <span class=\"kw\">$logger<\/span>);\n<span class=\"kw\">$user<\/span>       = <span class=\"fn\">CUser<\/span>::<span class=\"fn\">CreateInstance<\/span>(<span class=\"kw\">$request<\/span>, <span class=\"kw\">$connection_manager<\/span>, <span class=\"kw\">$cache<\/span>, <span class=\"kw\">$logger<\/span>);\n<span class=\"kw\">$response<\/span>   = <span class=\"fn\">CHtmlResponse<\/span>::<span class=\"fn\">CreateInstance<\/span>(<span class=\"kw\">$request<\/span>, <span class=\"kw\">$cache<\/span>, <span class=\"kw\">$user<\/span>, <span class=\"kw\">$logger<\/span>);\n<span class=\"fn\">CTranslator<\/span>::<span class=\"fn\">Init<\/span>(<span class=\"kw\">$connection_manager<\/span>, <span class=\"kw\">$cache<\/span>, <span class=\"kw\">$logger<\/span>);\n    <\/div>\n\n    <h3 class=\"mt-4\">Componenti che usano la cache<\/h3>\n    <div class=\"table-responsive\">\n      <table class=\"table table-striped\">\n        <thead class=\"table-dark\">\n          <tr><th>Componente<\/th><th>Cosa viene cachato<\/th><\/tr>\n        <\/thead>\n        <tbody>\n          <tr><td><code>CDB<\/code><\/td><td>Tutti i risultati <code>GetCached*()<\/code>; strutture tabelle; liste moduli<\/td><\/tr>\n          <tr><td><code>CBaseModel<\/code><\/td><td>Definizioni colonne; mappature picklist; risultati per viste lista\/dettaglio<\/td><\/tr>\n          <tr><td><code>CBaseController<\/code><\/td><td>Risposte OPTIONS (strutture moduli)<\/td><\/tr>\n          <tr><td><code>CUser<\/code><\/td><td>Permessi ACL, dati ruolo utente<\/td><\/tr>\n          <tr><td><code>CRoutes<\/code><\/td><td>Mappature tabella rotte (percorsi origine\/destinazione)<\/td><\/tr>\n          <tr><td><code>CTranslator<\/code><\/td><td>Stringhe di traduzione per lingua<\/td><\/tr>\n        <\/tbody>\n      <\/table>\n    <\/div>\n    <p><strong>Nota:<\/strong> <code>HTTP_CACHE_TIMEOUT<\/code> (predefinito: <code>0<\/code>) controlla il caching HTTP lato browser tramite l&#39;header <code>Cache-Control<\/code>. Questo &egrave; indipendente dal sistema cache lato server descritto qui.<\/p>\n  <\/div>\n<\/section>\n\n<section class=\"da-section\">\n  <div class=\"container\">\n    <h2>Buone pratiche<\/h2>\n    <ol>\n      <li><strong>Usare <code>\/dev\/shm\/<\/code> per la cache su file<\/strong> &ndash; Il percorso predefinito memorizza i file in un filesystem RAM. Fornisce latenza prossima a zero. Al riavvio <code>\/dev\/shm\/<\/code> viene svuotato automaticamente.<\/li>\n      <li><strong>Cambiare la chiave di crittografia<\/strong> &ndash; La chiave predefinita &egrave; un segnaposto. Sostituirla con una stringa casuale crittograficamente sicura: <code>openssl rand -base64 32<\/code><\/li>\n      <li><strong>Attenzione al limite di 64 KB<\/strong> &ndash; <code>CACHE_MAX_SIZE_PER_ELEMENT<\/code> &egrave; 65536 byte. I dati pi&ugrave; grandi non vengono cachati silenziosamente. Aumentare il limite o paginare le query.<\/li>\n      <li><strong>Redis per distribuzioni multi-server<\/strong> &ndash; <code>CFileCache<\/code> memorizza sul filesystem locale. Per deploy bilanciati o clusterizzati, usare <code>CRedisCache<\/code> per coerenza della cache.<\/li>\n      <li><strong>Debug con file di log<\/strong> &ndash; File cache: <code>core\/logs\/dressapi-cache.log<\/code>, Redis cache: <code>core\/logs\/dressapi-redis-cache.log<\/code><\/li>\n      <li><strong>Abilitare in produzione<\/strong> &ndash; La cache &egrave; disabilitata per default. Abilitarla in staging e produzione per guadagni significativi di prestazioni.<\/li>\n    <\/ol>\n  <\/div>\n<\/section>\n\n<section class=\"da-section text-center\">\n  <a href=\"\/tutorials\" class=\"btn btn-outline-primary\">&larr; Torna ai Tutorial<\/a>\n<\/section>","description":"Cache su filesystem e Redis con connessione lazy","meta_keywords":"","visible":"yes","status":"public","creation_date":"0000-00-00","id__user":1,"priority":0,"groupname":"TUTORIAL"}],"module":"pages","permissions":{"can_read":true},"main_item_title":"title","related_tables":[{"name":"Newsonpage","module":"newsonpage","FK":"id_cmspage","permissions":[]}],"page_info":{"table":"","key":"id","url":"\/pages","url_list":"\/pages","filters":{"id":["=","7008"]},"pagination":{"total_items":23,"page":1,"total_pages":2,"step":1,"items_per_page":20,"url_prev":"\/pages","url_next":"\/pages\/p\/2","url_tmpl":"\/pages\/p\/[[p]]","url_tmpl_with_order":"\/pages\/p\/[[p]]\/ob\/[[ob]]"},"order":{"field":"id","type":"DESC","url_tmpl":"\/pages\/p\/[[p]]"},"module":{"title":"Pages","description":"List of pages","name":"pages"},"current":{"title":"Pages","description":"List of pages","name":"pages","url":"\/tutorials\/cache"}},"components":{"alert":{"alert1":[{"type":"warning","message":"OK COMPONENT!"},{"type":"danger","message":"ERROR COMPONENT!"}]}},"messages":[]}