Skip to content

[Communication] Mehrere Plugins an einem Trunk: eingehende Anrufe zuordnen und Kapazität teilen #159

Description

@BechsteinDigital

Worum es geht

Mehrere Plugins sollen sich einen Trunk teilen. Ein Contact Center betreibt einen Outbound-Dialer und
eine Support-Hotline, dazu vielleicht Telefon-Einwahl in Konferenzen und ein Softphone im Browser —
alle über dieselbe SIP-Registrierung, weil ein Trunk mit Rufnummernblock der Normalfall ist und nicht
die Ausnahme.

Die Fähigkeiten dafür sind da: ICommunicationChannelRegistry ist workspace-scoped, jedes Plugin kommt
an die Channels, hängt sich an IncomingCall und kann über PlaceCallAsync selbst wählen. Was fehlt,
sind die zwei Regeln, die aus „alle können alles" ein geordnetes Nebeneinander machen.

Beide gehören hierher, weil kein Plugin sie für sich entscheiden kann: Keines kann festlegen, dass ein
eingehender Anruf ihm gehört, und keines kann sich selbst ein faires Kapazitätsbudget geben.


Teil A — Eingehende Anrufe zuordnen

Heute

IncomingCallObserver meldet jeden eingehenden Anruf, und jeder Konsument, der sich an
ICommunicationChannel.IncomingCall gehängt hat, bekommt ihn. Wer zuerst AcceptAsync ruft, gewinnt.
Das ist ein Rennen, kein Routing — und bei zwei Konsumenten im selben Workspace ist es nicht
entscheidbar, wer gewinnen sollte.

Erschwerend: ICall.Target ist der Anrufer. Die gerufene Nummer gibt es im Vertrag nicht. Ein
Plugin könnte also nicht einmal dann korrekt filtern, wenn es wollte.

Was fehlt

Rufnummern am Account. SipAccount trägt heute Id, Workspace, Verbindung,
MaxConcurrentCalls und Enabled — aber keine Rufnummern. Ein Workspace kann gar nicht ausdrücken,
welche DIDs er hat.

Die gerufene Nummer im Vertrag. Auf IncomingCallEventArgs oder ICall, damit überhaupt
entscheidbar ist, wem ein Anruf gilt.

Eine Zuordnung Nummer → Konsument, pro Workspace konfiguriert. Im Kern eine kleine
PBX-Routingtabelle: Diese Durchwahl gehört der Support-Hotline, jene der Konferenz-Einwahl.

Beobachten ist nicht Besitzen

Die wichtigste Unterscheidung, und sie sollte im Vertrag stehen, nicht in einer Konvention:

  • Ein Besitzer bekommt die Anrufe seiner Nummern und entscheidet über Annehmen und Abweisen. Genau
    einer pro Nummer.
  • Ein Beobachter — Aufzeichnung, Analyse, Wallboard, Compliance — sieht alle Anrufe und
    entscheidet nichts. Beliebig viele.

Heute sind beide dasselbe Event, und deshalb kann ein Beobachter versehentlich einen Anruf annehmen.

Zu entscheiden

  • Keine Zuordnung trifft zu. Ablehnen ist ehrlicher als von allen ignorieren zu lassen, und der
    Ablehnungscode sollte derselbe Gedanke sein wie bei IQuiescableChannel: 503 schickt den Carrier
    zur nächsten Route, 486 gibt dem Anrufer ein Besetztzeichen.
  • Besitzer ist nicht aktiv (Plugin deaktiviert, im Workspace nicht verfügbar). Nicht stillschweigend
    an den Nächstbesten fallen lassen — das wäre dieselbe Klasse Fehler wie ein Identitätsanbieter, der
    bei Deinstallation auf anonym zurückfällt.
  • Zwei Nummern, ein Besitzer muss gehen (eine Hotline mit mehreren Durchwahlen). Umgekehrt nicht.

Teil B — Kapazität teilen

Heute

MaxConcurrentCalls steht auf SipAccount, wird beim Anlegen validiert und liegt in der Datenbank —
und wird beim Wählen nirgends geprüft. CallControlService liest es nicht. Gebremst wird heute nur
vom Carrier.

Solange ein Plugin telefoniert, fällt das nicht auf. Sobald mehrere es tun, ist es ein Betriebsunfall
mit Ansage: Ein Outbound-Dialer belegt alle dreißig Leitungen und die Support-Hotline ist
unerreichbar.

Kontingente statt getrennter Accounts

Der naheliegende Reflex — jedes Plugin bekommt seinen eigenen Account — löst es schlechter. Bei einem
Trunk mit Rufnummernblock ist das technisch kaum sinnvoll teilbar, und die Kapazität wird starr
halbiert: Der Dialer bekommt fünfzehn und lässt sie brachliegen, während die Hotline an ihren fünfzehn
ansteht.

Besser ist ein Account mit Kontingenten: „Der Dialer darf höchstens zwanzig der dreißig belegen, zehn
bleiben eingehend reserviert." Getrennte Accounts bleiben richtig, wo der Unterschied wirklich außen
liegt — anderer Carrier, andere Marke, anderer Vertrag.

Zu entscheiden

  • Reservieren, nicht zählen. Zwei gleichzeitige Wählversuche dürfen nicht beide gewinnen, weil
    beide vor dem Hochzählen geprüft haben.
  • Eingehende Anrufe zählen mit. Sie belegen dieselben Leitungen. Ein Kontingent, das nur Ausgehendes
    begrenzt, schützt die Hotline nicht.
  • Verhalten bei Erschöpfung. Ausgehend: benannter Fehler, kein stiller Fehlschlag. Eingehend: 503,
    aus demselben Grund wie oben.
  • Reservierung überlebt den Anrufer nicht. Ein Plugin, das abstürzt oder deaktiviert wird, darf
    keine Leitungen dauerhaft binden.

Akzeptanzkriterien

Teil A

  • Ein Account trägt seine Rufnummern
  • Die gerufene Nummer ist bei einem eingehenden Anruf im Vertrag verfügbar
  • Eine Nummer hat genau einen Besitzer; die Zuordnung ist pro Workspace konfigurierbar
  • Besitzer und Beobachter sind im Vertrag getrennt — ein Beobachter kann keinen Anruf annehmen
  • Ein Anruf ohne passende Zuordnung wird definiert abgelehnt, nicht stillschweigend fallengelassen
  • Ein inaktiver Besitzer führt nicht dazu, dass ein anderer Konsument den Anruf übernimmt

Teil B

  • MaxConcurrentCalls wird beim Wählen durchgesetzt
  • Die Prüfung reserviert atomar; zwei gleichzeitige Versuche können das Limit nicht überschreiten
  • Eingehende Anrufe zählen auf dasselbe Limit
  • Kontingente pro Konsument sind konfigurierbar, mit dem Account-Limit als Obergrenze
  • Erschöpfung meldet sich benannt (ausgehend) bzw. mit 503 (eingehend)
  • Eine Reservierung wird freigegeben, wenn der haltende Konsument endet oder deaktiviert wird

Referenzen

  • custom/static-plugins/Communication/src/Application/Calls/IncomingCallObserver.cs — meldet, entscheidet
    ausdrücklich nicht („that is a consumer plugin's (e.g. a PBX's) decision")
  • custom/static-plugins/Communication/src/Domain/Accounts/SipAccount.csMaxConcurrentCalls ohne Durchsetzung
  • custom/static-plugins/Communication/src/Infrastructure/Sdk/SdkVoiceChannel.cs — 503-statt-486-Begründung
  • Künftige Konsumenten: Telefon-Einwahl, Softphone im Surface, Dialer, CRM

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions