Ho condotto un esperimento di scrittura di software per analizzare i risultati di una esportazione delle mie conversazioni con ChatGPT. Ma ho deciso di aiutarmi nell’analisi con un Large Language Model che sicuramente sa fare molto bene questo lavoro e in più con un LLM in locale, per vedere come funziona.
Negli ultimi giorni ho avuto problemi intermittenti con un mouse Bluetooth Trust (modello 24059-02) connesso al mio HP con Ubuntu. Il mouse ha funzionato regolarmente per un paio di giorni, poi improvvisamente ha smesso di …
Ho installato la versione 24.3.1.347 di Oracle SQL Developer però ho una sorpresa: sembra non sia possibile raggruppare le connessioni in cartelle. Risposta: Eh sì — è una sorpresa che ha colpito parecchi utenti anche …
Quando si esegue uno script Bash ci sono due modalità principali da impiegare, che hanno conseguenze diverse sull’ambiente della shell. 1. Esecuzione diretta (./script.sh) Quindi può solo leggere le variabili di ambiente della shell madre …
Preso dalla curiosità di ripassare l’assembly (tanti anni fa studiai il Motorola 68000) ho scritto una guida ordinata che illustra passo per passo come scrivere un semplice programma Assembly in real-mode (16 bit), confezionarlo come …
Ho reinstallato Mysql Workbench versione Community, un tool per l’amministrazione e la gestione di database basati su DBMS MySQL/MariaDB, su Ubuntu 20.04.
Al tentativo di creare una nuova connessione mi sono ritrovato un errore mai comparso prima:
An AppArmor policy prevents this sender from sending this message to this recipient; type="method_call", sender=":1.125"
eccetera.
Il problema (spiegato qui) è che ho dovuto installare MySQL con snap anziché apt, per cui mi sono scontrato con il fatto che i pacchetti installati con snap sono sandboxed, in qualche modo isolati e non possono fare alcune richieste al sistema. Ho dovuto quindi applicare questo comando:
Cos’è una congettura? È una proposizione che si presume vera ma non si è in grado di dimostrare rigorosamente. Le congetture affascinano perché funzionano per tutti i tentativi che si fanno per verificarla, cioè per trovare una dimostrazione di ciò che dicono. E questa loro non dimostrabilità le avvolge di mistero. Facciamo un piccolo recap.
Assiomi o postulati
Sono delle affermazioni così elementari che non possono essere dedotte da affermazioni ancora più semplici. A volte non sembrano molto elementari però si decide di arrestare il processo deduttivo perché non porterebbe più da nessuna parte se non girare il discorso utilizzando dei sinonimi. Ad esempio Assioma I della geometria euclidea:
Tra due punti qualsiasi è possibile tracciare una e una sola retta.
Qui si potrebbe obiettare “cos’è la retta? cos’è un punto? Il mio professore di Analisi I, Ezio Stagnaro, ci fece questo esempio:
Potete sostituire alla parola “punto” la parola “scarpe” e alla parola “retta” la parola “uomo” e verificare se detto così l’assioma funziona. Se poi riuscite a far funzionare l’analogia per tutti gli altri assiomi, vi accorgerete che punti e rette non sono oggetti in sé ma possono essere istanziati nella realtà in qualunque modo questo consenta di non pervenire a contraddizioni.
Proposizioni e teoremi
A partire dai fatti elementari (assiomi) costruiamo affermazioni più complesse (“La somma degli angoli interni di un triangolo è congruente con un angolo piatto” per rimanere nella Geometria Euclidea) che fanno discedere un fatto complesso (tesi) ad un certo numero finito di premesse (ipotesi). Il processo deduttivo che porta dalle ipotesi alla tesi senza saltare passaggi è detto dimostrazione del teorema. Se un teorema ha una dimostrazione l’affermazione è vera all’interno del sistema assiomatico e non può essere negata.
… e le congetture?
Se non riusciamo a trovare la dimostrazione siamo in una terra di mezzo. La proposizione è verificata in un numero più o meno grande di casi, sembra tenere, ma siccome non si possono provare tutti gli infiniti casi, non possiamo essere certi che funzioni sempre. Questa sicurezza ce la può dare solo una dimostrazione rigorosa.
Gli attacchi che i matematici sferrano verso queste proposizioni “maledette” a volte hanno qualcosa di epico, perché sono in grado di resistere per secoli all’assalto di menti sopraffine.
Per citarne un’altra di celebre, oltre a quella di Collatz, basti la congettura di Riemann, secondo la quale gli zeri non banali della funzione Zeta di Riemann hanno tutti parte reale uguale a 1/2. Anche di questa non si è ancora trovata una dimostrazione, benché fosse uno dei problemi inseriti da David Hilbert nell’elenco dei 23 problemi ancora da risolvere alla fine del XIX secolo (il problema numero 8, per la precisione).
Non è nemmeno provato che queste proposizioni non si possono dimostrare, almeno ci si metterebbe l’anima in pace. La proverbiale “pietra sopra” ad esempio è stata messa in un caso famoso: dopo secoli di tentativi da parte di matematici più o meno bravi di fornire una dimostrazione, si è trovato che l’assioma 5 della geometria euclidea – l’assioma delle parallele – non si poteva dimostrare come se fosse un teorema ma che anzi si poteva anche negare, addirittura in due modi, originando due nuovi sistemi indpendenti: le due geometrie non euclidee, quella di Riemann e quella di Bolyai/Lobacevskij.
In generale è stato dimostrato da Kurt Gödel che questo assioma è un punto debole come un punto debole ce l’ha qualsiasi altro sistema assiomatico (teoremi di incompletezza). Quindi in questo Gödel ha messo una pietra sopra sul fatto che in ogni sistema assiomatico ci sia un’affermazione che non può essere né dimostrata né negata all’interno dello stesso sistema.
Infatti si costruisce un altro sistema con una negazione del V postulato che ad esempio generi la geometria iperbolica e anche all’interno di questa non è dimostrabile né confutabile. Lo stesso vale per la geometria ellittica.
Non c’è niente da fare. Però questo teorema ha fatto finalmente in modo che la questione si potesse considerare chiusa una volta per sempre.
Ma in questo caso invece, come disse Paul Erdős, matematico ungherese, a proposito della congettura di Collatz
“La matematica non è ancora pronta per risolvere problemi del genere”
Paul Erdős
La congettura
La congettura di Collatz afferma che la successione così definita:
converge sempre a 1 per n \ge N \in \mathbb{N} . Ricordo che x_n \mod 2 = 0 vuol dire che x_n è pari e x_n \mod 2 = 1 vuol dire che x_n è dispari.
Python: la libreria matplotib
Ora per divertirmi un po’ a vedere come funziona questa successione ho utilizzato Python e la libreria matplotlib che avevo già utilizzato per lo studio dell’apprendimento delle reti neurali.
È semplice installarla come libreria stand-alone per Python 3:
Dopodiché la uso in un programma che semplicemente calcola la successione e la visualizza su un grafico
!/usr/bin/python3
import matplotlib.pyplot as plt
a = input("Dimmi un numero: ")
print("Successione di Collatz a partire da a=",a)
z = int(a)
s = []
s.append(z)
while (z != 1):
print(z,", ")
if (z % 2 == 0):
z /= 2
else:
z = 3*z + 1
s.append(z)
# la congettura d Collatz afferma che si esce sempre dal ciclo
# disegna
x = range(len(s))
plt.plot(x, s, label="Successione di Collatz")
plt.xlabel('iterazioni')
plt.ylabel('successione')
plt.title('Congettura di Collatz')
plt.legend()
plt.grid(True)
plt.show()
Ci si può divertire a vedere il comportamento di questa succcessione che sembra davvero imprevedibile per numeri che non fanno parte della successsione delle potenze di 2. Ad esempio il 103 genera questo andamento
Utilizzo il mio smartphone come hotspot wireless per connettermi a Internet con i dispositivi di casa. Stamattina ho voluto controllare quale fosse la potenza del segnale Wi-Fi perché volevo trovare la posizione ottimale dell’hotspot rispetto alla mia postazione di lavoro.
Prima mi sono studiato come accedere al dato della potenza del segnale Wi-Fiin ingresso dell’antenna (dettagli qui) e poi ho riportato questo dato in funzione del tempo in un grafico animato in modo tale da poter girar per casa e posizionarlo dove ho un segnale più forte, e non è detto che sia accanto al PC 😉
Campionamento della potenza del segnale Wi-Fi
La potenza istantanea del segnale Wi-Fi si può visualizzare con il comando
$ iwconfig wlp2s0 wlp2s0 IEEE 802.11 ESSID:"HUAWEI Y6 2019" Mode:Managed Frequency:2.412 GHz Access Point: F2:E4:A2:93:8C:0C Bit Rate=72.2 Mb/s Tx-Power=22 dBm Retry short limit:7 RTS thr:off Fragment thr:off Power Management:on Link Quality=61/70 Signal level=-49 dBm Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0 Tx excessive retries:171 Invalid misc:9250 Missed beacon:0
Come si vede, la linea
Link Quality=61/70 Signal level=-49 dBm
contiene l’informazione desiderata. Se spegniamo l’hotspot l’output del comando è invece:
$ iwconfig wlp2s0 wlp2s0 IEEE 802.11 ESSID:off/any Mode:Managed Access Point: Not-Associated Tx-Power=22 dBm Retry short limit:7 RTS thr:off Fragment thr:off Power Management:on
Quindi un modo se volete un po’ grezzo di accedere velocemente alla potenza del segnale Wi-Fi è fare il parsing dell’output di questo comando. L’idea è di lanciarlo con regolarità in modo da costruire un campione di dati da visualizzare in qualche modo in tempo reale.
Il primo programma che ho scritto è questo:
#!/usr/bin/python3
#
# Found in: https://www.calazan.com/how-to-continuously-monitor-your-wi-fis-signal-strength-in-ubuntu/
# Mutatis mutandi
#
import subprocess
import time
import argparse
parser = argparse.ArgumentParser(description='Display WLAN signal strength.')
parser.add_argument(dest='interface', nargs='?', default='wlan0',
help='wlan interface (default: wlan0)')
args = parser.parse_args()
print('\n---Press CTRL+Z or CTRL+C to exit---\n')
while True:
cmd = subprocess.Popen('iwconfig %s' % args.interface, shell=True,
stdout=subprocess.PIPE)
for line in cmd.stdout:
l = str(line)
if 'Link Quality' in l:
print (l),
elif 'Not-Associated' in l:
print ('No signal')
time.sleep(1)
Aspetti importanti di questo script sono il package subprocess che consente a Python di lanciare un comando del sistema operativo, in questo caso
iwconfig wlp2s0
Il package argparser fornisce una serie di utilissime funzioni per maneggiare le linee di comando (definire i parametri di avvio, la loro obbligatorietà, i valori di default e finanche la documentazione di help).
Il package itertools fornisce il metodo count() che realizza un contatore progressivo che ci serve per etichettare i campioni di segnale, campioni che vengono prelevati con regolarità utilizzando le funzione del package time..
In sostanza il metodo subprocess.Popen() lancia il comando e il dato desiderato si trova nell’output del comando che comunque non è una stringa e va convertito per potere fare il parsing. Il parsing si utilizza quando il dato è annegato nella sporcizia, per cui ritengo questo mio metodo un metodo piuttosto sporco.
In questo embrione di programma ho il seguente ouput
—Press CTRL+Z or CTRL+C to stop.—
b' Link Quality=70/70 Signal level=-31 dBm \n' b' Link Quality=70/70 Signal level=-31 dBm \n' b' Link Quality=70/70 Signal level=-36 dBm \n' b' Link Quality=70/70 Signal level=-32 dBm \n' b' Link Quality=70/70 Signal level=-32 dBm \n'
Il dato interessante si trova in ogni riga dalla posizione 45 alla posizione 48.
Con una piccola modifica ci facciamo stampare solo il dato desiderato
–--Press CTRL+Z or CTRL+C to stop.--- -32 -32 -35 -31 -31 -35 -35 -35 -36
Visualizzazione del segnale Wi-Fi utilizzando matplotlib
Ora invece di stampare questi valori, li voglio disporre su un grafico. Utilizzo la libreria matplotlib. Il sorgente completo è il seguente
#!/usr/bin/python3
#
# Adopted for reading wifi power signal and plotting it
#
# @author: marco@betaingegneria.it
# @copyright: GPL Affero v.3
# @date 2020-09-25
import subprocess
import argparse
import matplotlib.pyplot as plt
from itertools import count
from matplotlib.animation import FuncAnimation
# init graphics stuff
plt.style.use('seaborn')
x=[] # time series
y=[] # power samples
index = count()
# init wlan signal reader
# parse line command arguments
parser = argparse.ArgumentParser(description='Display WLAN signal strength.')
# interface
parser.add_argument(dest='interface', nargs='?', default='wlp2s0',
help='wireless.py [interface=wlp2s0] [delta=800]')
# sampling period in ms
parser.add_argument(dest='delta', nargs='?', default='800',
help='wireless.py [interface=wlp2s0] [delta=800]')
args = parser.parse_args()
if (int(args.delta) < 200):
print('Please, choose a value >= 200 for delta')
exit()
print('Close the window to exit')
def animate(i):
""" read the wifi power in dBm and plot """
x.append(next(index))
cmd = subprocess.Popen('iwconfig %s' % args.interface, shell=True,
stdout=subprocess.PIPE)
for line in cmd.stdout:
l = str(line)
if 'Link Quality' in l:
y.append(int(l[45:48]))
elif 'Not-Associated' in l:
y.append(None)
plt.cla()
plt.plot(x, y, '-', marker='.', label='WiFi signal strenght in dBm')
plt.legend(loc='upper left')
plt.tight_layout()
# ring a bell every sample
#print('\a')
ani = FuncAnimation(plt.gcf(), animate, interval=args.delta)
plt.show()
Un po’ di parafrasi: la funzione animate() contiene il codice che legge la potenza e, eseguendo il parsing estraiamo il valore in dBm da accodare alla lista y, mentre nella lista x accodiamo i valori del contatore.
Il plot viene fatto utilizzando come skin lo stile seaborn, che comprende la griglia in negativo (bianco su grigio).
Infine ho utilizzato il metodo matplotlib.animation() che può essere configurato per un aggiornamento ogni delta millisecondi – ci sganciamo quindi dalla libreria time – , variabile che gestiamo come parametro di input.
Il risultato è questo
Andamento della potenza segnale Wi-Fi provando a spostare l’hotspot in giro per la stanza
Se si spegne l’hotspot o si esce dal suo range di segnale non verrà plottato alcun dato.
Qualche tempo fa ho acquistato al CERN di Ginevra questa T-shirt che di tanto in tanto indosso:
La lagrangiana del Modello Standard
La formula scritta sulla maglietta è la lagrangiana del modello standard.
Invariabilmente, ogni volta che la indosso, mi chiedono: cos’è una lagrangiana? Oppure cosa vuol dire Modello Standard?
La cosa mi fa piacere perché ragionare di Fisica mi piace sempre un sacco.
La lagrangiana nella Meccanica Classica
Una breve introduzione alla lagrangiana nella Meccanica Classica la trovate qui. Meglio che la leggiate, vi serve per capire un po’ meglio quello che segue.
In sostanza è una funzione di posizione e velocità di un sistema meccanico classico che ci aiuta a scrivere le equazioni del moto.
La lagrangiana in Meccanica Quantistica
La lagrangiana della maglietta è un formalismo che si usa in teoria dei campi ed è l’estensione a questa disciplina del concetto che Lagrange elaborò per risolvere problemi di Meccanica Classica (lui non la chiamava così, siamo noi che abbiamo dovuto attaccare l’aggettivo “classica” per distinguerla dalla Meccanica Relativisitica o dalla Meccanica Quantistica) in sistemi con un numero finito di variabili – o gradi di libertà . Essa dal punto di vista matematico è un oggetto completamente diverso da quello della Meccanica Classica, è una funzione di densità: in questo caso la lagrangiana si applica ai continui e ai campi, che hanno un numero infinito di gradi di libertà.
A grandi linee ogni riga della maglietta è un pezzo della teoria del Modello Standard, il modello che spiega tre delle 4 forze fondamentali dell’Universo:
la forza elettromagnetica – che ti fa tenere il cane per il guinzaglio,
la forza forte, che tiene insieme i nuclei degli atomi,
la forza debole, che permette alle particelle di trasformarsi,
La forza gravitazionale, che ci tiene attacati per terra e move il Sole e l’altre stelle (cit.), finora è la grande esclusa dal Modello Standard.
La lagrangiana prevede 6 termini.
Primo termine -\frac{1}{4}F_{\mu\nu}F^{\mu\nu}
Questo termine è il prodotto scalare del tensore dell’intensità di campo F_{\mu\nu} contenente la codifica matematica di tutte le particelle di interazione – eccetto il bosone di Higgs -, dove \mu e \nu sono indici di Lorentz che rappresentano le componenti dello spaziotempo (t, x, y, z). Contiene la formulazione necessaria affinché queste particelle esistano e descrive come interagiscono tra loro. I contenuti differiscono a seconda delle proprietà delle particelle di interazione. Ad esempio, i fotoni, le particelle di interazione dell’interazione elettromagnetica, non possono interagire tra loro, perché non hanno carica elettrica. Pertanto, il contributo dell’interazione elettromagnetica consiste solo in un termine cinetico, che è la base dell’esistenza di fotoni liberi. La descrizione dei gluoni e dei bosoni dell’interazione debole comprende invece anche i termini di interazione oltre ai termini cinetici. I gluoni, ad esempio, sono essi stessi carichi di “colore” e possono quindi anche interagire tra loro.
Questo porta a una conseguenza entusiasmante: il Modello Standard della fisica delle particelle prevede l’esistenza di stati legati costituiti solo da gluoni, le cosiddette ‘glueballs‘ (letteralmente: palle di colla). Tuttavia, finora nessun esperimento ha rilevato le glueballs.
Il termine i\bar{\psi} D\!\!\!\!/\psi
Questo termine descrive invece come le particelle di interazione interagiscono con le particelle di materia. I campi \bar{\psi} e \psi descrivono (anti)quark e (anti)leptoni. Questo termine ad esempio regola l’annichilazione elettrone-positrone e il decadimento beta.
Il termine h.c.
h.c. sta per hermitian conjugate, coniugato hermitiano: un operatore o una matrice è hermitiana quando il suo prodotto con la controparte complessa coniugata è una quantità reale.
Siccome quando facciamo misure vediamo numeri reali, è necessario che tutte le quantità complesse abbiano il corrispettivo coniugato in modo tale che il prodotto sia un numero reale. Questo è un assioma della Meccanica Quantistica e da’ conto del fatto che le misurazioni sono sempre numeri reali. Il termine h.c. va eventualmente aggiunto al secondo termine (interazione bosone – particella) qualora questo dia un numero complesso. In realtà il secondo termine è sempre un numero reale (più precisamente è un operatore autoaggiunto). Lo chiamano anche hot coffee (caffè che scotta).
Piccolo dizionario: i bosoni (particelle che seguono la statistica di Bose-Einstein)sono le particelle mediatrici della forza (la più famosa è il fotone, che veicola l’interazione elettromagnetica). I fermioni (particelle che seguono la statistica di Fermi-Dirac) sono le particelle di materia ordinaria (protoni ed elettroni per esempio).
Il termine \psi_i y_{ij}\psi_j\phi
Il quarto termine descrive l’accoppiamento di una particella di materia con il campo di Higgs\phi per acquisire massa. y_{ij} è detto campo di Yukawa, vedi bibliografia.
Il termine |D_\mu\phi|^2
Il quinto termine descrive l’accoppiamento di una particella di interazione con il campo di Higgs.
Tuttavia le uniche particelle di interazione (dette anche bosoni) che hanno massa sono i bosoni dell’interazione debole – previste teoricamente da Abdus Salam e scoperte da Carlo Rubbia, per cui questo termine descrive proprio loro (W^{\pm}, Z_0).
Il termine -V(\phi)
L’ultimo termine descrive il potenziale del campo di Higgs (detto più precisamente campo di Brout–Englert–Higgs).
Nella maggior parte delle cose che ci accadono quotidianamente, l’unico termine ad entrare in azione è il secondo. I fotoni non ottengono massa dal meccanismo di Higgs, mentre i gluoni sono privi di massa perché non si accoppiano al campo di Brout-Englert-Higgs.
Come vedete in questa lagrangiana non si parla di gravità. includere la gravità nella teoria di campo quantistica è una delle sfide che stanno impegnando i fisici e i matematici da mezzo secolo e ancora non si vede la luce in fondo al tunnel.
Inoltre il modello standard descrive perfettamente la materia, ma ciò che vediamo è solo il 5% della materia totale, per cui il resto cos’è e come funziona?
Per chi volesse approfondire, in bibliografia c’è un bell’articolo, senza addentrarsi troppo nei dettagli.
Fatemi siete arrivati fin qui se vi è piaciuto l’articolo.
Ringrazio Corrado Tagliente per avermi spronato a migliorare l’articolo.
e così via, cioè anziche venire aggiunto l’ultimo elemento (anche lui una lista) alla lista, venivano sostituiti tutti gli elementi della lista con l’ultima lista calcolata.
Il problema è che stavo copiando ogni volta il riferimento alla variabile w (che è lo stesso ad ogni ciclo) e non il contenuto della variabile stessa (che cambia ad ogni ciclo):
[<riferimento a w>, <riferimento a w>, …]
Per disinnescare l’errore ho fatto così:
for i in range(1,5):
w=P.learn()
print(type(w))
hist_w.append(w[:])
print("hist_w", hist_w)
.gitignore o .git/info/exclude? Vediamo le differenze e il perché a volte Git sembra ignorare le nostre direttive sull’esclusione di alcuni file dal ciclo di vita del software.
Differenza tra .gitignore e .git/info/exclude
Entrambi i file servono a Git per stabilire quali file non entrano nel ciclo di versionamento.
Essi agiscono ad un diverso livello.
Il file .git/info/exclude serve per escludere i file nel proprio clone locale; se qualcuno che collabora con noi non ha escluso gli stessi file che abbiamo escluso noi, potremmo ritrovarci – se facciamo un clone o un fetch – file che sono entrati nel versionamento nell’origin ma che io non ho perché esclusi, o viceversa. Quindi è bene, quando si devono escludere dei file globalmente (per tutti i programmatori) agire sul file .gitignore che invece viene versionato – per cui ogni repository locale se lo trova. Se dobbiamo agire in modo più granulare sui file da versionare (o meno) directory per directory, è possibile scrivere un apposito file .gitgnore in ogni directory in cui serve.
Git sembra ignorare la direttiva di ignorare file
Ho messo dei file da escludere nel file .git/info/exclude (o in .gitignore) ma continuo a vederli come new file quando eseguo il comando git status:
$ git status
Sul branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: app/Http/Controllers/BaseController.php
new file: storage/logs/laravel-2020-07-22.log
new file: storage/logs/laravel-2020-07-23.log
...
In particolare non voglio che i file di log entrino nel versionamento. Ho però inserito correttamente la riga
storage/logs*
in entrambi i file .gitignore e .git/info/exclude. Come mai Git continua a mostrarmeli come file da versionare?
Git utilizza il file .gitignore nel momento in cui aggiunge i file; una volta aggiunti all’indice dei file da versionare Git continua a considerarli tali, a dipetto di modifiche successive dei file di gitignore. Quindi ci sono due modi per uscire dall’impasse:
cancellare fisicamente i file, ma è l’opzione più stupida git rm storage/logs/*
cancellare la cache: git rm --cached storage/logs/*
Infatti l’opzione --cached indica a Git di togliere i file dall’indice ma non dal file system.
Ho trovato queste indicazioni in un paio di risposte di fourm 1 e 2.
Negoziare una connessione con un database è una operazione non banale che richiede una consistente attività per la realizzazione del collegamento sicuro, autenticato e la messa a disposizione delle risorse del database all’applicazione che ne fa uso. Ho esperienza di questo fatto soprattutto in ambito Java e Oracle ma le considerazioni da fare sono tutto sommato agnostiche e quindi indipendenti dalle piattaforme utilizzate. Tuttavia gli esempi sono in Java.
In ambito concorrente, come una applicazione web, questa operazione di negoziazione rischia presto di diventare un collo di bottiglia se si rimane nello schema 1 richiesta = 1 nuova connessione a DB.
Per risolvere questo problema è stato messa a punto la tecninca Oracle connection pooling (ovvero qui vediamo quella di Oracle, ma in realtà è una tecnica che si può applicare anche ad altri DBMS).
L’idea è quella di utilizzare una coda (in questo ambito definita come pool) nella quale mantenere un numero finito di connessioni riutilizzabili che vengono in continuazione prelevate dalla coda quando servono e rimesse nella coda al termine del loro utilizzo.
Oracle Connection Pooling: il caso d’uso
Supponiamo che arrivino due richieste quasi simultanee:
Richiesta 1 (es. https://mysite.com/authenticate) la quale richiede collegamento al db per effettuare una autenticazione utente: in questa richiesta viene aperta una nuova connessione al database e viene lanciata la query sulla tabella utenti. Dopo aver acqusito il recordset, l’applicazione può dismettere la connessione invocando il metodo close()
Richiesta 2 (es. https://mysite.com/articles/get/AB123) che deve eseguire una lettura di una tabella. L’applicazione apre una connessione ex novo (con il conseguente impegno di risorse e di tempo), lancia l’interrogazione e recupera il recordset.
Non sarebbe più efficiente per la Richiesta 2, qualora arrivasse dopo la conclusione della prima richiesta, riutilizzare la connessione aperta dalla Richiesta 1? Se la richiesta evitasse di chiamare la close() e invece “mettesse via” la connessione da qualche parte, la richiesta 2 potrebbe riutilizzarla senza dover negoziare un’altra volta con il DBMS username/password e caricamento driver.
Oracle connection Pooling: funzionamento
Una struttura dati efficiente per mettere via dati che si ritiene di usare di nuovo a breve, può essere una coda, in particolare una coda FIFO. Che nel gergo dei database si chiama Connection Pool. Questa coda conterrà descrittori di risorse DBMS.
Il funzionamento di questa coda è il seguente. Partiamo da una coda vuota.
Arriva il primo processo (una richesta HTTP: per esempio una login) che richiede all’applicazione di collegarsi al database. Siccome la coda è vuota, viene giocoforza negoziata una connessione nuova con il database, chiamiamola Connessione 1. Quando l’interrogazione è stata fatta e quindi la login è eseguita, la connessione non viene chiusa, bensì viene salvato il descrittore con un push() nella coda, che finora era ancora vuota.
Arriva il secondo processo che necessita di una connessione al DBMS e quello che fa prima di tutto è controllare se nella coda ci sono connessioni disponibili. Supponiamo che la login sia già stata fatta e quindi nella coda ci sia la connessione 1 rilasciata dal primo processo. Tutto ciò che deve fare il processo 2 è lanciare la query senza autenticarsi di nuovo; quindi serviamo due processi con una sola connessione.
Se, viceversa, il processo 2 arriva quando la login è ancora in corso, quindi la connessione 1 è ancora impegnata, in questo caso il processo 2 dovrà aprire una connessione 2.
Ma questo meccanismo fa sì che, dopo un po’ di tempo, la probabilità di trovare una connessione libera nella coda sia molto maggiore e quindi il numero di negoziazioni dirette è destinato a calare drasticamente.
Una strategia ancora migliore è questa: se all’avvio dell’applicazione server inizializzaziamo il pool costruendo e mettendo già via un certo numero di connessioni nella coda, risparmiamo tempo ad applicazione avviata.
Ogni volta che un processo preleva una connessione dalla coda, fa un pull(), ovvero toglie la connessione dalla coda; quando ha terminato, opera un push() e la rimette nella coda. Se il numero di processi concorrenti è maggiore della dimensione della coda fino a quel momento, viene negoziata una nuova connessione, altrimenti si fa più velocemente un pull().
Questo meccanismo deve essere gestito; ossia si deve sollevare l’applicazione dal negoziare nuove connessioni, perché dovrà essere tutto supervisionato dal processo di gestione della coda, che è un oggetto della classe ConnectionPool. L’applicazione dovrà solo limitarsi a chiedere una connessione al pool, e il pool in ogni caso gliela concederà (sia essa una connessione riciclata o una nuova di zecca), senza che l’applicazione debba interloquire direttamente con il database.
Per rendere poi più efficiente l’uso della memoria, si può fare a meno di allocare una coda dinamica (che implica continue chiamate alla JVM per farsi allocare memoria a runtime), ma utilizzzare invece un array di lunghezza fissa, in cui vengono impilate tante connessioni quante ne servono: all’inizio supponiamo che la coda sia vuota, viene fatto il push() di una nuova connessione, marcata con Active e consegnata al processo, che la potrà usare. Quando il processo che l’ha utilizzata la rilascia, il gestore del pool la dichiarerà Idle rimettendola a disposizione. Però non è che la inserisce nuovamente nel pool, semplicemente le cambia lo stato.
Tuttavia nell’implementazione di esempio che vedremo, viene utilizzata un’altra tecnica ancora: vengono impiegati due vettori; quello delle connessioni disponibili (o idle) e quello delle connessioni attive (active).
Il processo successivo che arriva a chiedere connessioni, si farà dare l’ultima connessione idle resasi disponibile (comportamento FIFO).
Oracle connection Pooling: esempio applicativo
Questo esempio è liberamente tratto (e semplificato) da Java Made So Easy.
Iniziamo dunque definendo la classe ConnectionPool:
package connectionPooling;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
public class ConnectionPool implements Runnable {
// i parametri di connessione
private String driver, url, username, password;
// la dimensione massima del pool
private int maxConnections;
// le 2 code delle connessioni idle e active
public Vector<Connection> idleConnections, activeConnections;
// il costruttore
public ConnectionPool(
String driver, String url, String username,
String password, int maxConnections, int initialConnections
) throws SQLException {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
this.initialConnections = initialConnections;
this.maxConnections = maxConnections;
// in questo esempio il pool è mantenuto su due vettori:
// - quello delle connessioni disponibili (o idle: pigre, nullafacenti);
// - quello delle connessioni impegnate (o active);
idleConnections = new Vector<Connection>(initialConnections);
activeConnections = new Vector<Connection>();
// qui opzionalmente preparo un certo numero di connessioni già pronte
for (int i = 0; i < initialConnections; i++) {
idleConnections.addElement(makeNewConnection());
}
}
Il metodo fondamentale qui è makeNewConnection() con il quale costruiamo una nuova connessione “vera”; ovviamente lo dichiariamo private per impedire al programma chiamante di negoziare connessioni direttamente con il DBMS attraverso di essa:
private Connection makeNewConnection() throws SQLException {
try {
// Carica il database driver
Class.forName(this.driver);
// Apri la connessione al db
Connection connection = DriverManager.getConnection(this.url, this.username,
this.password);
return (connection);
} catch (Exception cnfe) {
cnfe.printStackTrace();
throw new SQLException(
"ConnectionPool:: SQLException encountered:: "
+ cnfe.getMessage());
}
}
Come visto sopra questo metodo viene chiamato al deploy del connectioonPool per inizializzare un certo numero di connessioni già pronte per l’uso (che passo al costruttore).
Ora dobbiamo gestire il pool, ovvero consegnare al processo che ne fa richiesta una connessione idle, toglierla dal vettore delle idle e metterla in quello delle active:
/**
* Method to return Connections
*/
public synchronized Connection getConnection() throws SQLException {
if (!idleConnections.isEmpty()) {
Connection existingConnection = (Connection) idleConnections
.lastElement();
int lastIndex = idleConnections.size() - 1;
// toglie la connessione dal vettore delle pigre:
idleConnections.removeElementAt(lastIndex);
// ... e la aggiunge al vettore delle attive:
activeConnections.addElement(existingConnection);
return (existingConnection);
} else {
// se non ci sono più connessioni pigre, ne creo una di nuova:
return makeNewConnection();
}
}
Poniamo attenzione sul fatto che il metodo getConnection() è definito come synchronized, per cui molte richieste pressoché simultanee verranno arbitrate in modo che le connessioni siano consegnate distintamente secondo il loro ordine di arrivo.
Può accadere, alla fine, che i processi esauriscano tutte le connessioni del pool: in questo caso il vettore delle connessioni active sarà pieno e quello delle idle sarà vuoto; si potrebbe gestire anche questa situazione ma, in questa fase in cui cerchiamo di capire come funziona il meccanismo, semplicemente notifichiamo che il pool è pieno.
if ((totalConnections() >= maxConnections) {
throw new SQLException("Connection limit reached");
}
dove totalConnection() è un count degli elementi del vettore activeConnections più il count degli elementi del vettore idleConnections.
Alla fine il programma chiamante utilizzerà il metodo getConnection() per comunicare con il pool, mentre la classe ConnectionPool utilizzerà il metodo (privato) makeConnection() per comunicare con il DBMS.
Così abbiamo operato la separazione tra applicazione e DBMS.
Ci manca solo un metodo per liberare una connessione (cioè toglierla dal vettore delle connesioni active e rimetterla in quello delle connessione idle), lo chiamiamo free():
/**
* Method to free the Connections
*/
public synchronized void free(Connection connection) {
activeConnections.removeElement(connection);
idleConnections.addElement(connection);
}
Alla fine siamo pronti ad utilizzare questa infrastruttura nell’applicazione; dobbiamo creare un oggetto istanza della classe ConnectionPool che mettterà a disposizione connessioni già pronte prelevabili con il metodo getConnection():
package connectionPooling;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/** Copyright (c), Ankit Mittal JavaMadeSoEasy.com */
public class PreparedStatementUseConnectionPooling {
public static void main(String... arg) throws SQLException {
ConnectionPool connectionPool = new ConnectionPool(
"oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@localhost:1521:orcl",
"foo",
"bar",
10, 5);
Connection dbConn = connectionPool.getConnection();
System.out.println("Sono pronte le connessioni dalla classe ConnectionPool");
PreparedStatement prepStmt = dbConn
.prepareStatement("select ID, NAME from EMPLOYEE");
ResultSet rs = prepStmt.executeQuery();
while (rs.next()) {
System.out.print(rs.getInt("ID") + " ");
System.out.println(rs.getString("NAME"));
}
if (rs != null)
rs.close(); // close resultSet
if (prepStmt != null)
prepStmt.close(); // close PreparedStatement
connectionPool.free(dbConn);
System.out.println("Rilasciata la connessione dalla classe ConnectionPool");
}
}
Si noti che il programma chiamante, dopo la connessione impartisce comandi direttamente al DBMS; il ConnectioPool si intromette tra di loro solo al momento dell’acquisizione di nuove connessioni (o della rimozione di un connessione che non serve più).
Per ulteriori dettagli implementativi si faccia riferimento all’articolo citato all’inizio.
Adesso il funzionamento di un pool dovrebbe essere almeno un pelino più chiaro. Almeno per me lo è stato.
Ho sistemato un errore che si era manifestato nel cruscotto dei dati relativi alla pandemia tuttora in corso.
L’errore si è verificato in seguito ad una modifica del tracciato dati messo online dalla Protezione Civile e aveva temporaneamente reso non operativo il cruscotto.
Potete nuovamente consultarli (dati nazionali e regionali) a questo link:
Esercizio Python 7 – freccia verso destra (right arrow)
"""
print right arrow
*
* *
* * *
* *
*
e.g.
0 1 2 3 4 5 6 7 8 9 i test N-1-i
0 * 0, j<=0 9
1 * * 1, j<=1 8
2 * * * 2, j<=2 7
3 * * * * 3, j<=3 6
4 * * * * * 4, j <5 5
5 * * * * 5, j <4 4
6 * * * 6, j <3 3
7 * * 7, j <2 2
8 * 8, j <1 1
9 9, j <0 0
"""
print("right arrow")
for i in range(N):
for j in range(N):
if j <= i and j < N - 1 - i:
print("*"),
else:
print(" "),
print(" ") # a capo
Ho stampato un tracing e ho riportato tutti i valori delle variabili per le varie configurazioni disegnate. Come si vede, si tratta di combinare le condizioni che compaiono negli esercizi 3 e 6 (BLT e TLT), solo che sostituisco la condizione j <= N - 1 - i con j < N - 1 - i in modo da non disegnare la diagonale BLTR che risulterebbe col disegnare una freccia “spuntata”.
Esercizio Python 8 – freccia verso sinistra (left arrow)
"""
print left arrow
*
* *
* * *
* *
*
e.g.
0 1 2 3 4 5 6 7 8 9 i test N-1-i
0 * 0, j>=9 9*
1 * * 1, j>=8 8*
2 * * * 2, j>=7 7*
3 * * * * 3, j>=6 6*
4 * * * * * 4, j>=5 5*
5 * * * * *5, j> 5 4
6 * * * *6, j> 6 3
7 * * *7, j> 7 2
8 * *8, j> 8 1
9 *9, j> 9 0
"""
print("left arrow")
for i in range(N):
for j in range(N):
if j >= i and j > N - 1 - i:
print("*"),
else:
print(" "),
print(" ") # a capo
Per girare la freccia a sinistra è necessario ribaltare entrambe le condizioni di test:
da j<=i a j>=i
da j <= N - 1 - i a j > N - 1 - i
L’assenza dell’= sulla seconda condizione, al solito, evita il disegno della doppia punta.
Possiamo indovinare che anche per le frecce up e down si dovrà fare lo stesso…
Esercizio Python 9 – freccia verso l’alto (upward arrow)
"""
print upward arrow
*
* * *
* * * * *
e.g.
0 1 2 3 4 5 6 7 8 9
0
1
2
3
4 N-1-i test i
5 * 4 <= j < 5
6 * * * 3 <= j < 6
7 * * * * * 2 <= j < 7
8 * * * * * * * 1 <= j < 8
9 * * * * * * * * * 0 <= j < 9
"""
print("upward arrow")
for i in range(N):
for j in range(N):
if N - i - 1 <= j < i:
print("*"),
else:
print(" "),
print(" ") # a capo
Qui è ormai evidente: è l’intersezione dei due esercizi BLT e BRT (esercizi 3 e 5). L’ultimo esercizio è la freccia verso il basso e, ormai, tutti gli arcani dovrebbero essere svelati e possiamo dire “com’era facile!”
Esercizio Python 10 – freccia verso il basso (downward arrow)
"""
print downward arrow
* * * * *
* * *
*
e.g.
0 1 2 3 4 5 6 7 8 9 i test N-1-i
0 * * * * * * * * * 0 <=j< 9
1 * * * * * * * 1 <=j< 8
2 * * * * * 2 <=j< 7
3 * * * 3 <=j< 6
4 * 4 <=j< 5
5
6
7
8
9
"""
print("downward arrow")
for i in range(N):
for j in range(N - i):
if i <= j < N - 1 - i:
print("*"),
else:
print(" "),
print(" ") # a capo
Spero abbiate trovato divertenti questi piccoli esercizi (sì… anche se alla fine sono un po’ silly).
Li ho messi in quest’ordine perché sono due metà del quadrato diviso dalla stessa diagonale (TLBR nel primo, TRBL nel secondo).
Esercizio 3 – disegnare un triangolo BLT
print("BLT")
for i in range(N):
for j in range(N):
if i >= j:
print("*"),
else:
print(" "),
print(" ") # a capo
Qui è evidente che l’esercizio 3 si ricava dall’esercizio 1 semplicemente sostituendo la condizione i=j con i>=j ma anche estendendo la scansione di j a tutto l’intervallo 0..N-1.
Esercizio 4 – disegnare un triangolo TRT
print("TRT")
for i in range(N):
for j in range(N):
if i <= j:
print('*'),
else:
print(" "),
print(" ") # a capo
Anche qui è quasi ovvio, la condizione i <= j sostituisce la precedente i >= j (con la condizione = specifichiamo che va disegnata anche la diagonale).
Esercizio 5 – disegnare un triangolo BRT
print("BRT")
for i in range(N):
for j in range(N):
if j >= N - 1 - i:
print("*"),
else:
print(" "),
print(" ") # a capo
Anche qui basta sostituire la condizione j = N - 1 - i con la relazione >=, a partire dall’esercizio 2.
Esercizio 6 – disegnare un triangolo TLT
print("TLT")
for i in range(N):
for j in range(N):
if j <= N - 1 - i:
print("*"),
else:
print(" "),
print(" ")
Ormai avete capito la regola, non serve spaccarsi la testa.
I pattern generati da questi programmi sono riportati in alto, all’inizio dell’articolo, nello stesso ordine.
Ok, abbiamo finito.! Con la prossima e ultima parte disegneremo frecce.
Altri articoli del blog
Vi lascio anche i riferimenti per le altre puntate:
Utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Lo facciamo per migliorare l'esperienza di navigazione e per mostrare annunci personalizzati. Il consenso a queste tecnologie ci consentirà di elaborare dati quali il comportamento di navigazione o gli ID univoci su questo sito. Il mancato consenso o la revoca del consenso possono influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Commenti recenti