In questa sezione

Separazione delle responsabilità

Il sistema distingue due fasi:

  1. Validità del dispatch: questo candidato è legittimo per questa richiesta?
  2. 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:

  1. Corrispondenza esatta (dist = 0): la più preferita
  2. Raffinamento (dist > 0): il candidato specializza la richiesta
  3. 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:

  1. Trova i candidati validi tramite is_dispatchable
  2. Fra i candidati validi, verifica se qualcuno è is_equivalent alla cap preferita
  3. Se lo trova, lo seleziona indipendentemente dal ranking per specificità
  4. 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.