non-blocking initialization of bucket target notifications (#15571)

This commit is contained in:
Anis Elleuch
2022-09-28 01:23:28 +01:00
committed by GitHub
parent 94dbb4a427
commit 86bb48792c
21 changed files with 827 additions and 732 deletions
+70 -68
View File
@@ -18,7 +18,6 @@
package target
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
@@ -36,7 +35,7 @@ import (
)
const (
reconnectInterval = 5 // In Seconds
reconnectInterval = 5 * time.Second
storePrefix = "minio"
)
@@ -107,6 +106,8 @@ func (m MQTTArgs) Validate() error {
// MQTTTarget - MQTT target.
type MQTTTarget struct {
lazyInit lazyInit
id event.TargetID
args MQTTArgs
client mqtt.Client
@@ -120,13 +121,15 @@ func (target *MQTTTarget) ID() event.TargetID {
return target.id
}
// HasQueueStore - Checks if the queueStore has been configured for the target
func (target *MQTTTarget) HasQueueStore() bool {
return target.store != nil
}
// IsActive - Return true if target is up and active
func (target *MQTTTarget) IsActive() (bool, error) {
if err := target.init(); err != nil {
return false, err
}
return target.isActive()
}
func (target *MQTTTarget) isActive() (bool, error) {
if !target.client.IsConnectionOpen() {
return false, errNotConnected
}
@@ -147,7 +150,7 @@ func (target *MQTTTarget) send(eventData event.Event) error {
}
token := target.client.Publish(target.args.Topic, target.args.QoS, false, string(data))
if !token.WaitTimeout(reconnectInterval * time.Second) {
if !token.WaitTimeout(reconnectInterval) {
return errNotConnected
}
return token.Error()
@@ -155,8 +158,12 @@ func (target *MQTTTarget) send(eventData event.Event) error {
// Send - reads an event from store and sends it to MQTT.
func (target *MQTTTarget) Send(eventKey string) error {
if err := target.init(); err != nil {
return err
}
// Do not send if the connection is not active.
_, err := target.IsActive()
_, err := target.isActive()
if err != nil {
return err
}
@@ -182,12 +189,16 @@ func (target *MQTTTarget) Send(eventKey string) error {
// Save - saves the events to the store if queuestore is configured, which will
// be replayed when the mqtt connection is active.
func (target *MQTTTarget) Save(eventData event.Event) error {
if err := target.init(); err != nil {
return err
}
if target.store != nil {
return target.store.Put(eventData)
}
// Do not send if the connection is not active.
_, err := target.IsActive()
_, err := target.isActive()
if err != nil {
return err
}
@@ -202,17 +213,12 @@ func (target *MQTTTarget) Close() error {
return nil
}
// NewMQTTTarget - creates new MQTT target.
func NewMQTTTarget(id string, args MQTTArgs, doneCh <-chan struct{}, loggerOnce logger.LogOnce, test bool) (*MQTTTarget, error) {
if args.MaxReconnectInterval == 0 {
// Default interval
// https://github.com/eclipse/paho.mqtt.golang/blob/master/options.go#L115
args.MaxReconnectInterval = 10 * time.Minute
}
func (target *MQTTTarget) init() error {
return target.lazyInit.Do(target.initMQTT)
}
if args.KeepAlive == 0 {
args.KeepAlive = 10 * time.Second
}
func (target *MQTTTarget) initMQTT() error {
args := target.args
// Using hex here, to make sure we avoid 23
// character limit on client_id according to
@@ -229,61 +235,57 @@ func NewMQTTTarget(id string, args MQTTArgs, doneCh <-chan struct{}, loggerOnce
SetTLSConfig(&tls.Config{RootCAs: args.RootCAs}).
AddBroker(args.Broker.String())
client := mqtt.NewClient(options)
target.client = mqtt.NewClient(options)
target := &MQTTTarget{
id: event.TargetID{ID: id, Name: "mqtt"},
args: args,
client: client,
quitCh: make(chan struct{}),
loggerOnce: loggerOnce,
token := target.client.Connect()
ok := token.WaitTimeout(reconnectInterval)
if !ok {
return errNotConnected
}
if token.Error() != nil {
return token.Error()
}
token := client.Connect()
retryRegister := func() {
for {
retry:
select {
case <-doneCh:
return
case <-target.quitCh:
return
default:
ok := token.WaitTimeout(reconnectInterval * time.Second)
if ok && token.Error() != nil {
target.loggerOnce(context.Background(),
fmt.Errorf("Previous connect failed with %w attempting a reconnect",
token.Error()),
target.ID().String())
time.Sleep(reconnectInterval * time.Second)
token = client.Connect()
goto retry
}
if ok {
// Successfully connected.
return
}
}
}
yes, err := target.isActive()
if err != nil {
return err
}
if !yes {
return errNotConnected
}
if target.store != nil {
streamEventsFromStore(target.store, target, target.quitCh, target.loggerOnce)
}
return nil
}
// NewMQTTTarget - creates new MQTT target.
func NewMQTTTarget(id string, args MQTTArgs, loggerOnce logger.LogOnce) (*MQTTTarget, error) {
if args.MaxReconnectInterval == 0 {
// Default interval
// https://github.com/eclipse/paho.mqtt.golang/blob/master/options.go#L115
args.MaxReconnectInterval = 10 * time.Minute
}
if args.KeepAlive == 0 {
args.KeepAlive = 10 * time.Second
}
var store Store
if args.QueueDir != "" {
queueDir := filepath.Join(args.QueueDir, storePrefix+"-mqtt-"+id)
target.store = NewQueueStore(queueDir, args.QueueLimit)
if err := target.store.Open(); err != nil {
target.loggerOnce(context.Background(), err, target.ID().String())
return target, err
store = NewQueueStore(queueDir, args.QueueLimit)
if err := store.Open(); err != nil {
return nil, fmt.Errorf("unable to initialize the queue store of MQTT `%s`: %w", id, err)
}
if !test {
go retryRegister()
// Replays the events from the store.
eventKeyCh := replayEvents(target.store, doneCh, target.loggerOnce, target.ID())
// Start replaying events from the store.
go sendEvents(target, eventKeyCh, doneCh, target.loggerOnce)
}
} else if token.Wait() && token.Error() != nil {
return target, token.Error()
}
return target, nil
return &MQTTTarget{
id: event.TargetID{ID: id, Name: "mqtt"},
args: args,
store: store,
quitCh: make(chan struct{}),
loggerOnce: loggerOnce,
}, nil
}