Files
Bridge-and-Join-s/internal/lkgateway/lkportfolio_all_live_test.go
T
fontvielle a0ffbfa721 ЛК: портфель ДЕПО «для всех клиентов» — одно соединение и один запрос к DEPO, свой цикл раз в 5 мин; итог в настройках свёрнут
На 9 после включения «для всех» (23.10.2026) каждый проход открывал по два
соединения с DEPO на каждый из 1158 договоров — около 50 входов в MSSQL в
секунду без перерыва, а проход шёл в цикле опроса заявок ЛК и задерживал его.

- Остатки всех клиентов — одним запросом (fsql.Dp03), одно соединение с DEPO
  на проход; по списку — запрос по клиенту на том же соединении. На боевой
  DEPO: общий запрос 127 мс, совпадает с запросом по клиенту (58 договоров).
- Свой цикл lkPortfolioLoop (здоровье «lk-portfolio»): по списку — каждые
  10 с, для всех клиентов — раз в 5 мин. Опрос заявок ЛК больше не ждёт.
- Проходы по очереди: фоновый и «Заполнить сейчас» не шлют один портфель
  дважды; принудительный проход пропускает отправленные за последнюю минуту.
- ЛК не отвечает — проход прерывается; одна и та же ошибка отправки не
  повторяется в журнале каждый проход. Ошибка чтения DEPO снимается, когда
  портфель снова читается без изменений.
- Настройки: итог (договоров, отправлено, нечего отправлять, с ошибкой),
  последние 5 отправок; договоры с ошибкой и все договоры — под раскрытием.
  «Отправлять нечего» — не ошибка (поле Nothing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q7eREaYwUt1i4fNLZcGnjn
2026-10-23 14:45:16 +03:00

78 lines
2.2 KiB
Go

//go:build live
package lkgateway
import (
"context"
"fmt"
"os"
"sort"
"strings"
"testing"
"time"
)
// Остатки «для всех клиентов» одним запросом (Dp03) совпадают с запросом по клиенту (Dp01). Только чтение DEPO.
// PF_SETUP — копия setup.json (по умолчанию /var/lib/bj/.bj/setup.json).
func TestLKPortfolioAllLive(t *testing.T) {
path := os.Getenv("PF_SETUP")
if path == "" {
path = "/var/lib/bj/.bj/setup.json"
}
rc, err := NewRuntimeConfig(path)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
fs := rc.Snapshot().Fancy
db, err := fancyOpen(fs, fs.DepoDB())
if err != nil {
t.Fatal(err)
}
defer db.Close()
t0 := time.Now()
prs, err := lkPortfolioAllPairs(ctx, rc, db)
if err != nil {
t.Fatal(err)
}
t1 := time.Now()
all, err := depoAllPositions(ctx, db)
if err != nil {
t.Fatal(err)
}
t2 := time.Now()
by := lkPortfolioByPort(all, prs)
rows := 0
for _, p := range all {
rows += len(p)
}
t.Logf("договоров %d (%s); остатков %d по %d портфелям (%s); с остатками у договоров %d",
len(prs), t1.Sub(t0).Round(time.Millisecond), rows, len(all), t2.Sub(t1).Round(time.Millisecond), len(by))
key := func(ps []DepoPosition) string {
var k []string
for _, p := range ps {
k = append(k, fmt.Sprintf("%s|%s|%g", strings.TrimSpace(p.AccountNum), strings.TrimSpace(p.ISIN), p.Quantity))
}
sort.Strings(k)
return strings.Join(k, ";")
}
step, checked := len(prs)/60+1, 0
var one time.Duration
for i := 0; i < len(prs); i += step {
pr := prs[i]
s := time.Now()
want, err := depoClientPositionsDB(ctx, db, pr.Depo)
one += time.Since(s)
if err != nil {
t.Fatalf("%s: %v", pr.Depo, err)
}
if a, b := key(want), key(by[pr.Depo]); a != b {
t.Errorf("%s ← %s: по клиенту %d строк, общим запросом %d\n %s\n %s", pr.LK, pr.Depo, len(want), len(by[pr.Depo]), a, b)
}
checked++
}
if checked > 0 {
t.Logf("сверено договоров %d, запрос по клиенту в среднем %s", checked, (one / time.Duration(checked)).Round(time.Millisecond))
}
}