mirror of
https://github.com/pgsty/minio.git
synced 2026-07-21 13:10:22 +03:00
Add an option to make bucket notifications synchronous (#17406)
With the current asynchronous behaviour in sending notification events to the targets, we can't provide guaranteed delivery as the systems might go for restarts. For such event-driven use-cases, we can provide an option to enable synchronous events where the APIs wait until the event is successfully sent or persisted. This commit adds 'MINIO_API_SYNC_EVENTS' env which when set to 'on' will enable sending/persisting events to targets synchronously.
This commit is contained in:
@@ -45,6 +45,7 @@ const (
|
||||
apiDisableODirect = "disable_odirect"
|
||||
apiGzipObjects = "gzip_objects"
|
||||
apiRootAccess = "root_access"
|
||||
apiSyncEvents = "sync_events"
|
||||
|
||||
EnvAPIRequestsMax = "MINIO_API_REQUESTS_MAX"
|
||||
EnvAPIRequestsDeadline = "MINIO_API_REQUESTS_DEADLINE"
|
||||
@@ -62,6 +63,7 @@ const (
|
||||
EnvAPIDisableODirect = "MINIO_API_DISABLE_ODIRECT"
|
||||
EnvAPIGzipObjects = "MINIO_API_GZIP_OBJECTS"
|
||||
EnvAPIRootAccess = "MINIO_API_ROOT_ACCESS" // default "on"
|
||||
EnvAPISyncEvents = "MINIO_API_SYNC_EVENTS" // default "off"
|
||||
)
|
||||
|
||||
// Deprecated key and ENVs
|
||||
@@ -135,6 +137,10 @@ var (
|
||||
Key: apiRootAccess,
|
||||
Value: "on",
|
||||
},
|
||||
config.KV{
|
||||
Key: apiSyncEvents,
|
||||
Value: "off",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -154,6 +160,7 @@ type Config struct {
|
||||
DisableODirect bool `json:"disable_odirect"`
|
||||
GzipObjects bool `json:"gzip_objects"`
|
||||
RootAccess bool `json:"root_access"`
|
||||
SyncEvents bool `json:"sync_events"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON - Validate SS and RRS parity when unmarshalling JSON.
|
||||
@@ -269,5 +276,7 @@ func LookupConfig(kvs config.KVS) (cfg Config, err error) {
|
||||
}
|
||||
cfg.StaleUploadsExpiry = staleUploadsExpiry
|
||||
|
||||
cfg.SyncEvents = env.Get(EnvAPISyncEvents, kvs.Get(apiSyncEvents)) == config.EnableOn
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@@ -104,5 +104,11 @@ var (
|
||||
Optional: true,
|
||||
Type: "boolean",
|
||||
},
|
||||
config.HelpKV{
|
||||
Key: apiSyncEvents,
|
||||
Description: "set to enable synchronous bucket notifications" + defaultHelpPostfix(apiSyncEvents),
|
||||
Optional: true,
|
||||
Type: "boolean",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -157,7 +157,7 @@ func (list *TargetList) TargetMap() map[TargetID]Target {
|
||||
}
|
||||
|
||||
// Send - sends events to targets identified by target IDs.
|
||||
func (list *TargetList) Send(event Event, targetIDset TargetIDSet, resCh chan<- TargetIDResult) {
|
||||
func (list *TargetList) Send(event Event, targetIDset TargetIDSet, resCh chan<- TargetIDResult, synchronous bool) {
|
||||
if atomic.LoadInt64(&list.currentSendCalls) > maxConcurrentTargetSendCalls {
|
||||
err := fmt.Errorf("concurrent target notifications exceeded %d", maxConcurrentTargetSendCalls)
|
||||
for id := range targetIDset {
|
||||
@@ -165,33 +165,40 @@ func (list *TargetList) Send(event Event, targetIDset TargetIDSet, resCh chan<-
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if synchronous {
|
||||
list.send(event, targetIDset, resCh)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
var wg sync.WaitGroup
|
||||
for id := range targetIDset {
|
||||
list.RLock()
|
||||
target, ok := list.targets[id]
|
||||
list.RUnlock()
|
||||
if ok {
|
||||
wg.Add(1)
|
||||
go func(id TargetID, target Target) {
|
||||
atomic.AddInt64(&list.currentSendCalls, 1)
|
||||
defer atomic.AddInt64(&list.currentSendCalls, -1)
|
||||
defer wg.Done()
|
||||
tgtRes := TargetIDResult{ID: id}
|
||||
if err := target.Save(event); err != nil {
|
||||
tgtRes.Err = err
|
||||
}
|
||||
resCh <- tgtRes
|
||||
}(id, target)
|
||||
} else {
|
||||
resCh <- TargetIDResult{ID: id}
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
list.send(event, targetIDset, resCh)
|
||||
}()
|
||||
}
|
||||
|
||||
func (list *TargetList) send(event Event, targetIDset TargetIDSet, resCh chan<- TargetIDResult) {
|
||||
var wg sync.WaitGroup
|
||||
for id := range targetIDset {
|
||||
list.RLock()
|
||||
target, ok := list.targets[id]
|
||||
list.RUnlock()
|
||||
if ok {
|
||||
wg.Add(1)
|
||||
go func(id TargetID, target Target) {
|
||||
atomic.AddInt64(&list.currentSendCalls, 1)
|
||||
defer atomic.AddInt64(&list.currentSendCalls, -1)
|
||||
defer wg.Done()
|
||||
tgtRes := TargetIDResult{ID: id}
|
||||
if err := target.Save(event); err != nil {
|
||||
tgtRes.Err = err
|
||||
}
|
||||
resCh <- tgtRes
|
||||
}(id, target)
|
||||
} else {
|
||||
resCh <- TargetIDResult{ID: id}
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Stats returns stats for targets.
|
||||
func (list *TargetList) Stats() TargetStats {
|
||||
t := TargetStats{}
|
||||
|
||||
@@ -249,7 +249,7 @@ func TestTargetListSend(t *testing.T) {
|
||||
for i, testCase := range testCases {
|
||||
testCase.targetList.Send(Event{}, map[TargetID]struct{}{
|
||||
testCase.targetID: {},
|
||||
}, resCh)
|
||||
}, resCh, false)
|
||||
res := <-resCh
|
||||
expectErr := (res.Err != nil)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user