Shell of the continuous changes feed -- calls out to a `send` function to deliver the change. This is called from BLIP connections as well as HTTP handlers, which is why this is not a method on `handler`.
(ctx context.Context, database *DatabaseCollectionWithUser, inChannels base.Set, options ChangesOptions, docIDFilter []string, send func([]*ChangeEntry) error)
| 1506 | // This is called from BLIP connections as well as HTTP handlers, which is why this is not a |
| 1507 | // method on `handler`. |
| 1508 | func GenerateChanges(ctx context.Context, database *DatabaseCollectionWithUser, inChannels base.Set, options ChangesOptions, docIDFilter []string, send func([]*ChangeEntry) error) (err error, forceClose bool) { |
| 1509 | // Set up heartbeat/timeout |
| 1510 | var timeoutInterval time.Duration |
| 1511 | var timer *time.Timer |
| 1512 | var heartbeat <-chan time.Time |
| 1513 | if options.HeartbeatMs > 0 { |
| 1514 | ticker := time.NewTicker(time.Duration(options.HeartbeatMs) * time.Millisecond) |
| 1515 | defer ticker.Stop() |
| 1516 | heartbeat = ticker.C |
| 1517 | } else if options.TimeoutMs > 0 { |
| 1518 | timeoutInterval = time.Duration(options.TimeoutMs) * time.Millisecond |
| 1519 | defer func() { |
| 1520 | if timer != nil { |
| 1521 | timer.Stop() |
| 1522 | } |
| 1523 | }() |
| 1524 | } |
| 1525 | |
| 1526 | if options.Continuous { |
| 1527 | options.Wait = true // we want the feed channel to wait for changes |
| 1528 | } |
| 1529 | |
| 1530 | if !options.Since.IsNonZero() { |
| 1531 | database.dbStats().CBLReplicationPull().NumPullReplSinceZero.Add(1) |
| 1532 | } |
| 1533 | |
| 1534 | var lastSeq SequenceID |
| 1535 | var feed <-chan *ChangeEntry |
| 1536 | var timeout <-chan time.Time |
| 1537 | var feedErr error |
| 1538 | |
| 1539 | // feedStarted identifies whether at least one MultiChangesFeed has been started. Used to identify when a one-shot changes is done. |
| 1540 | feedStarted := false |
| 1541 | |
| 1542 | loop: |
| 1543 | for { |
| 1544 | // If the feed has already been started once and closed, and this isn't a continuous |
| 1545 | // replication, we're done. |
| 1546 | if feedStarted && feed == nil && !options.Continuous { |
| 1547 | break loop |
| 1548 | } |
| 1549 | |
| 1550 | if feed == nil { |
| 1551 | // Refresh the feed of all current changes: |
| 1552 | if lastSeq.IsNonZero() { // start after end of last feed |
| 1553 | options.Since = lastSeq |
| 1554 | } |
| 1555 | if database.IsClosed() { |
| 1556 | forceClose = true |
| 1557 | break loop |
| 1558 | } |
| 1559 | if len(docIDFilter) > 0 { |
| 1560 | feed, feedErr = database.DocIDChangesFeed(ctx, inChannels, docIDFilter, options) |
| 1561 | } else { |
| 1562 | feed, feedErr = database.MultiChangesFeed(ctx, inChannels, options) |
| 1563 | } |
| 1564 | if feedErr != nil || feed == nil { |
| 1565 | return feedErr, forceClose |
no test coverage detected