using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace MeterVault.Infrastructure.Persistence.Migrations { /// public partial class BindUnboundMqttSourcesToSoleBroker : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { // MQTT routing now honours meter_source.endpoint_id (SDD §6.1): a source is served only // by the broker it is bound to. Previously an unbound source was subscribed on every // broker and matched on topic alone, so unbound sources that work today would silently // go quiet after this deploy. // // Backfill them onto the single broker only when exactly one exists — then the old // "any broker" behaviour and the new "its broker" behaviour are the same thing, so the // rewrite is provably lossless. With zero brokers there is nothing to bind to; with two // or more the old behaviour was already ambiguous and a guess could route a meter's // data to the wrong broker, so those are left for the operator to resolve in the UI. // // Enums persist as their C# names (HasConversion), hence 'Mqtt'/'MqttBroker'. // HomeAssistant sources are deliberately excluded: the HA workers have always required // endpoint_id, so an unbound HA source is already inert and binding it here would // activate ingestion the operator never had running. migrationBuilder.Sql(""" UPDATE meter_source AS s SET endpoint_id = sole.id FROM (SELECT id FROM ingestion_endpoint WHERE type = 'MqttBroker') AS sole WHERE s.endpoint_id IS NULL AND s.source_type IN ('Mqtt', 'Tasmota') AND (SELECT count(*) FROM ingestion_endpoint WHERE type = 'MqttBroker') = 1; """); } /// protected override void Down(MigrationBuilder migrationBuilder) { // Intentionally empty. The rows this bound are indistinguishable from ones the operator // bound by hand, so clearing endpoint_id on the way down would discard real // configuration. Leaving the binding in place is harmless under the old routing, which // ignored endpoint_id entirely. } } }