|
1 | 1 | package ynabber
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "log/slog" |
4 | 5 | "strconv"
|
5 | 6 | "time"
|
6 | 7 | )
|
7 | 8 |
|
8 | 9 | type Ynabber struct {
|
9 | 10 | Readers []Reader
|
10 | 11 | Writers []Writer
|
| 12 | + |
| 13 | + config *Config |
| 14 | + logger slog.Logger |
| 15 | +} |
| 16 | + |
| 17 | +// NewYnabber creates a new Ynabber instance |
| 18 | +func NewYnabber(config *Config) *Ynabber { |
| 19 | + return &Ynabber{ |
| 20 | + config: config, |
| 21 | + logger: *slog.Default(), |
| 22 | + } |
11 | 23 | }
|
12 | 24 |
|
13 | 25 | type Reader interface {
|
@@ -53,3 +65,62 @@ func (m Milliunits) String() string {
|
53 | 65 | func MilliunitsFromAmount(amount float64) Milliunits {
|
54 | 66 | return Milliunits(amount * 1000)
|
55 | 67 | }
|
| 68 | + |
| 69 | +// Run starts Ynabber by reading transactions from all readers into a channel to |
| 70 | +// fan out to all writers |
| 71 | +func (y *Ynabber) Run() { |
| 72 | + batches := make(chan []Transaction) |
| 73 | + |
| 74 | + // Create a channel for each writer and fan out transactions to each one |
| 75 | + channels := make([]chan []Transaction, len(y.Writers)) |
| 76 | + for c := range channels { |
| 77 | + channels[c] = make(chan []Transaction) |
| 78 | + } |
| 79 | + go func() { |
| 80 | + for batch := range batches { |
| 81 | + for _, c := range channels { |
| 82 | + c <- batch |
| 83 | + } |
| 84 | + } |
| 85 | + }() |
| 86 | + |
| 87 | + for c, writer := range y.Writers { |
| 88 | + go func(writer Writer, batches <-chan []Transaction) { |
| 89 | + for batch := range batches { |
| 90 | + err := writer.Bulk(batch) |
| 91 | + if err != nil { |
| 92 | + y.logger.Error("writing", "error", err, "writer", writer) |
| 93 | + } |
| 94 | + } |
| 95 | + }(writer, channels[c]) |
| 96 | + } |
| 97 | + |
| 98 | + for _, r := range y.Readers { |
| 99 | + go func(reader Reader) { |
| 100 | + for { |
| 101 | + start := time.Now() |
| 102 | + batch, err := reader.Bulk() |
| 103 | + if err != nil { |
| 104 | + y.logger.Error("reading", "error", err, "reader", reader) |
| 105 | + continue |
| 106 | + } |
| 107 | + batches <- batch |
| 108 | + y.logger.Info("run succeeded", "in", time.Since(start)) |
| 109 | + |
| 110 | + // TODO(Martin): The interval should be controlled by the |
| 111 | + // reader. We are only pausing the entire reader goroutine |
| 112 | + // because thats how the config option is implemented now. |
| 113 | + // Eventually we should move this option into the reader |
| 114 | + // allowing for multiple readers with different intervals. |
| 115 | + if y.config.Interval > 0 { |
| 116 | + y.logger.Info("waiting for next run", "in", y.config.Interval) |
| 117 | + time.Sleep(y.config.Interval) |
| 118 | + } else { |
| 119 | + break |
| 120 | + } |
| 121 | + } |
| 122 | + }(r) |
| 123 | + } |
| 124 | + |
| 125 | + select {} |
| 126 | +} |
0 commit comments