** * Watch watches the key and bucket and calls the callback function for each message received. * The callback will be called once for each individual message in the batch. * * @param ctx - the context for the watch, we provide the context as the way of cancel manually watcher * @param bucket
(ctx context.Context, bucket string, key []byte, cb func(message *Message) error, opts ...WatchOptions)
| 1193 | * @return error - the error if the watch is stopped |
| 1194 | */ |
| 1195 | func (db *DB) Watch(ctx context.Context, bucket string, key []byte, cb func(message *Message) error, opts ...WatchOptions) (*Watcher, error) { |
| 1196 | watchOpts := NewWatchOptions() |
| 1197 | |
| 1198 | if len(opts) > 0 { |
| 1199 | watchOpts = &opts[0] |
| 1200 | } |
| 1201 | |
| 1202 | if db.watchMgr == nil { |
| 1203 | return nil, ErrWatchFeatureDisabled |
| 1204 | } |
| 1205 | |
| 1206 | subscriber, err := db.watchMgr.subscribe(bucket, string(key)) |
| 1207 | if err != nil { |
| 1208 | return nil, err |
| 1209 | } |
| 1210 | |
| 1211 | watchingFunc := func() error { |
| 1212 | maxBatchSize := 128 |
| 1213 | batch := make([]*Message, 0, maxBatchSize) |
| 1214 | |
| 1215 | // Use a ticker to process the batch every 100 milliseconds |
| 1216 | // Avoid CPU busy spinning |
| 1217 | ticker := time.NewTicker(100 * time.Millisecond) |
| 1218 | keyWatch := string(key) |
| 1219 | |
| 1220 | processBatch := func(batch []*Message) error { |
| 1221 | if len(batch) == 0 { |
| 1222 | return nil |
| 1223 | } |
| 1224 | |
| 1225 | for _, msg := range batch { |
| 1226 | errChan := make(chan error, 1) |
| 1227 | go func(msg *Message) { |
| 1228 | errChan <- cb(msg) |
| 1229 | }(msg) |
| 1230 | |
| 1231 | select { |
| 1232 | case <-time.After(watchOpts.CallbackTimeout): |
| 1233 | return ErrWatchingCallbackTimeout |
| 1234 | case err := <-errChan: |
| 1235 | if err != nil { |
| 1236 | return err |
| 1237 | } |
| 1238 | } |
| 1239 | } |
| 1240 | return nil |
| 1241 | } |
| 1242 | |
| 1243 | defer func() { |
| 1244 | ticker.Stop() |
| 1245 | |
| 1246 | if subscriber != nil && subscriber.active.Load() { |
| 1247 | if err := db.watchMgr.unsubscribe(bucket, keyWatch, subscriber.id); err != nil { |
| 1248 | // ignore the error |
| 1249 | } |
| 1250 | } |
| 1251 | }() |
| 1252 |