Separazione delle responsabilità
Il sistema distingue due fasi:
- Validità del dispatch: questo candidato è legittimo per questa richiesta?
- Ranking: fra i candidati validi, quale dovrebbe essere selezionato?
Questo documento tratta il ranking. La validità del dispatch è definita in Dispatch.
Il ranking si applica solo ai candidati validi per il dispatch. Non fare mai il ranking prima della validazione.
L’insieme valido
Data una richiesta r, si definisce l’insieme valido:
Valid(r) = { p ∈ C | Dispatch(p, r) }
Il ranking è un ordine totale su Valid(r).
Distanza di specificità
Definizione
Per un candidato p e una richiesta r:
dist(p, r) = spec_C(p) - spec_C(r)
dove spec_C è la specificità del cap URN (vedi Specificità).
Interpretazione
| Distanza | Significato | Relazione del candidato |
|---|---|---|
dist = 0 |
Equivalente | Il candidato corrisponde esattamente alla richiesta |
dist > 0 |
Raffinamento | Il candidato è più specifico |
dist < 0 |
Ripiego | Il candidato è più generico |
Ordine di preferenza
Policy standard
La policy di ranking predefinita preferisce:
- Corrispondenza esatta (
dist = 0): la più preferita - Raffinamento (
dist > 0): il candidato specializza la richiesta - Ripiego (
dist < 0): il candidato è generico, ultima risorsa
All’interno di ciascuna categoria, si preferisce la distanza assoluta minore.
Ordinamento formale
Per i candidati a e b in Valid(r):
a ≺ b (a preferito a b) sse:
dist(a,r) ≥ 0 ∧ dist(b,r) < 0 # a è un raffinamento, b è un ripiego
∨ (dist(a,r) ≥ 0 ∧ dist(b,r) ≥ 0 ∧ dist(a,r) < dist(b,r)) # entrambi raffinamenti, a più vicino
∨ (dist(a,r) < 0 ∧ dist(b,r) < 0 ∧ |dist(a,r)| < |dist(b,r)|) # entrambi ripieghi, a più vicino
Regola semplificata
preferred = min(valid_candidates, key=ranking_key)
def ranking_key(p, r):
d = dist(p, r)
if d >= 0:
return (0, d) # fascia dei raffinamenti, preferisci il positivo minore
else:
return (1, -d) # fascia dei ripieghi, preferisci il negativo minore
Risoluzione dei pareggi
Quando più candidati hanno la stessa distanza:
Policy predefinita
Vince il primo registrato: viene selezionato il candidato registrato per primo.
Policy alternative
Alcuni sottosistemi risolvono i pareggi in modo diverso:
| Sottosistema | Policy |
|---|---|
| UrnMatcher | Prima per specificità, poi per ordine di registrazione |
| CapMatrix | Confronto stretto > (nessun pareggio possibile) |
| CapBlock | Confronto stretto > (nessun pareggio possibile) |
| RelaySwitch | Vince il primo registrato |
Evitare i pareggi
Progetta le cap in modo da evitare i pareggi, garantendo specificità distinte:
- Aggiungi tag distintivi
- Usa convenzioni di denominazione coerenti
- Registra le cap più specifiche prima di quelle generiche
Esempi
Si preferisce la corrispondenza esatta
Richiesta: cap:in=media:pdf;extract;out=media:record
spec = 3
Candidati validi:
A: cap:in=media:pdf;extract;out=media:record spec=3, dist=0
B: cap:in=media:pdf;extract;out=media:record;v=2 spec=4, dist=+1
C: cap:extract;in=media:;out=media: spec=1, dist=-2
Ranking: A (dist=0) ≺ B (dist=+1) ≺ C (dist=-2)
Scelto: A
Raffinamento quando non c’è una corrispondenza esatta
Richiesta: cap:convert;in=media:;out=media:
spec = 1
Candidati validi:
A: cap:in=media:pdf;convert;out=media:html spec=3, dist=+2
B: cap:in=media:image;convert;out=media:png spec=3, dist=+2
C: cap:convert;in=media:;out=media: spec=1, dist=0
Ranking: C (dist=0) ≺ {A, B} (dist=+2, pari merito)
Scelto: C
Se C non è disponibile:
Ranking: {A, B} a pari merito con dist=+2
Scelto: il primo registrato fra A e B
Solo ripieghi
Richiesta: cap:in=media:pdf;v=2.0;extract;out=media:record;format=json
spec = 5
Candidati validi:
A: cap:in=media:pdf;extract;out=media:record spec=3, dist=-2
B: cap:extract;in=media:;out=media: spec=1, dist=-4
Ranking: A (dist=-2) ≺ B (dist=-4)
Scelto: A (più vicino alla richiesta pur essendo meno specifico)
Indicazioni di cap preferita
Preferenza dell’utente
Il sistema supporta un’indicazione facoltativa di cap preferita:
RelaySwitch::route_request(request, preferred_cap: Option<&CapUrn>)
Quando viene fornito preferred_cap:
- Trova i candidati validi tramite
is_dispatchable - Fra i candidati validi, verifica se qualcuno è
is_equivalentalla cap preferita - Se lo trova, lo seleziona indipendentemente dal ranking per specificità
- Altrimenti ripiega sul ranking normale
Casi d’uso
- L’utente richiede esplicitamente una versione specifica di una capacità
- La configurazione specifica un candidato particolare
- Test con un handler noto
Proprietà
Determinismo
A parità di:
- Richiesta
- Insieme dei candidati registrati
- Ordine di registrazione
il candidato selezionato è deterministico.
Stabilità
Aggiungendo un nuovo candidato P al sistema:
- Se P non è in
Valid(r), la selezione non cambia - Se P è in
Valid(r), P può essere selezionato solo se ha un ranking più alto
Monotonia
Se il candidato p è selezionato per la richiesta r, allora p resta selezionato per qualsiasi richiesta r' per cui:
r' ⪯ r(r’ è più specifica di r)Dispatch(p, r')vale ancora
Note di implementazione
Selezione efficiente
Per insiemi di candidati piccoli, una scansione lineare è sufficiente:
fn select_best(candidates: &[CapUrn], request: &CapUrn) -> Option<&CapUrn> {
let request_spec = request.specificity();
candidates.iter()
.filter(|p| p.is_dispatchable(request))
.min_by_key(|p| {
let dist = p.specificity() as isize - request_spec as isize;
if dist >= 0 {
(0, dist)
} else {
(1, -dist)
}
})
}
Caching
Per le richieste interrogate di frequente, metti in cache:
- L’insieme valido
Valid(r) - Il ranking ordinato
- Il candidato selezionato
Invalida la cache alla registrazione o deregistrazione di un candidato.
Relazione con is_comparable
Il predicato is_comparable di Predicati si usa talvolta per la scoperta, ma non per il ranking:
is_comparable(p, r) ⟹̸ Dispatch(p, r)
Candidati comparabili possono non essere idonei al dispatch. Verifica sempre prima is_dispatchable.
Riepilogo
| Concetto | Definizione |
|---|---|
| Insieme valido | { p | Dispatch(p, r) } |
| Distanza | spec_C(p) - spec_C(r) |
| Preferenza | Esatta (0) > Raffinamento (+) > Ripiego (-) |
| Pareggio | Il primo registrato (predefinito) |
Il ranking è policy, non semantica. Il predicato di dispatch definisce la validità; il ranking determina la selezione fra le opzioni valide. Una volta che una sequenza di cap è stata risolta in un flusso di lavoro, quel flusso di lavoro viene codificato come grafo e serializzato tramite la notazione delle macchine.