Реализация rule-engine-service: RabbitMQ → правила из PostgreSQL → gRPC
Слушает telemetry.new_reading (ручной ack/nack, реквизишн только при транспортных ошибках gRPC), кэширует активные automation_rules в памяти с периодическим обновлением из PostgreSQL (JOIN с devices — резолвит внутренние ID в external_id, которым оперируют MQTT/Redis/gRPC). Условия правил (>,<,>=,<=,=,!=) оцениваются обобщённо, без привязки к конкретным типам устройств. При срабатывании вызывает device-control-service по gRPC и публикует automation.rule_triggered в RabbitMQ. Проверено сквозным тестом через docker compose на полном пайплайне: mosquitto_pub → ingest-service (ClickHouse + telemetry.new_reading) → rule-engine-service (совпадение правила) → device-control-service (gRPC → Redis desired_state + MQTT-команда) → automation.rule_triggered. Показание ниже порога проверено отдельно — правило корректно не срабатывает.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package rulecache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// activeRulesQuery resolves condition_source_device_id/target_device_id (FKs
|
||||
// into devices.id) to devices.external_id, since every other part of the
|
||||
// platform (MQTT, Redis, gRPC) keys devices by their external ID, not
|
||||
// PostgreSQL's internal bigserial ID.
|
||||
const activeRulesQuery = `
|
||||
SELECT
|
||||
r.id AS rule_id,
|
||||
r.zone_id AS zone_id,
|
||||
src.external_id AS source_external_id,
|
||||
tgt.external_id AS target_external_id,
|
||||
r.condition_sensor_type AS condition_sensor_type,
|
||||
r.condition_operator AS condition_operator,
|
||||
r.condition_value AS condition_value,
|
||||
r.action_type AS action_type,
|
||||
r.action_params AS action_params
|
||||
FROM automation_rules r
|
||||
JOIN devices src ON src.id = r.condition_source_device_id
|
||||
JOIN devices tgt ON tgt.id = r.target_device_id
|
||||
WHERE r.is_active = true
|
||||
`
|
||||
|
||||
type PostgresFetcher struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewPostgresFetcher(pool *pgxpool.Pool) *PostgresFetcher {
|
||||
return &PostgresFetcher{pool: pool}
|
||||
}
|
||||
|
||||
func (f *PostgresFetcher) FetchActiveRules(ctx context.Context) ([]Row, error) {
|
||||
dbRows, err := f.pool.Query(ctx, activeRulesQuery)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query active rules: %w", err)
|
||||
}
|
||||
rows, err := pgx.CollectRows(dbRows, pgx.RowToStructByName[Row])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scan active rules: %w", err)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
Reference in New Issue
Block a user