Skip to content

Commit 98aa627

Browse files
authored
Add lock to stream pull to prevent multiple nodes starting pulling for the same stream (#1187)
1 parent 12704f7 commit 98aa627

File tree

3 files changed

+56
-15
lines changed

3 files changed

+56
-15
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ require (
1717
github.com/hashicorp/serf v0.10.1
1818
github.com/julienschmidt/httprouter v1.3.0
1919
github.com/lib/pq v1.10.9
20-
github.com/livepeer/go-api-client v0.4.18
20+
github.com/livepeer/go-api-client v0.4.19
2121
github.com/livepeer/go-tools v0.3.6
2222
github.com/livepeer/joy4 v0.1.1
2323
github.com/livepeer/livepeer-data v0.8.1

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,12 @@ github.com/livepeer/go-api-client v0.4.18-0.20240305122931-8f6d8c6543ad h1:eSqYA
451451
github.com/livepeer/go-api-client v0.4.18-0.20240305122931-8f6d8c6543ad/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw=
452452
github.com/livepeer/go-api-client v0.4.18 h1:grDZK6oMBm/6N9ZqsAk4ae2ohGDVzRPd2U12DrTRv2s=
453453
github.com/livepeer/go-api-client v0.4.18/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw=
454+
github.com/livepeer/go-api-client v0.4.19-0.20240321152123-03b5d097f6f0 h1:qJAVOO2lzdRSuL4GBgvpvSCXA/OUq1yOxpwZ9Nv/Xis=
455+
github.com/livepeer/go-api-client v0.4.19-0.20240321152123-03b5d097f6f0/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw=
456+
github.com/livepeer/go-api-client v0.4.19-0.20240325114751-e7cb002ff24d h1:DeO2VY8L2trThto7tc5KyiSjq+rTyLTnEz8je73kv6E=
457+
github.com/livepeer/go-api-client v0.4.19-0.20240325114751-e7cb002ff24d/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw=
458+
github.com/livepeer/go-api-client v0.4.19 h1:9YBSdYlYhCZdap08mIvptOR9B3Gf46AQOrWObJhvlkA=
459+
github.com/livepeer/go-api-client v0.4.19/go.mod h1:Jdb+RI7JyzEZOHd1GUuKofwFDKMO/btTa80SdpUpYQw=
454460
github.com/livepeer/go-tools v0.3.6 h1:LhRnoVVGFCtfBh6WyKdwJ2bPD/h5gaRvsAszmCqKt1Q=
455461
github.com/livepeer/go-tools v0.3.6/go.mod h1:qs31y68b3PQPmSr8nR8l5WQiIWI623z6pqOccqebjos=
456462
github.com/livepeer/joy4 v0.1.1 h1:Tz7gVcmvpG/nfUKHU+XJn6Qke/k32mTWMiH9qB0bhnM=

handlers/geolocation/geolocation.go

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ package geolocation
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"math/rand"
78
"net/http"
89
"net/url"
910
"regexp"
1011
"strconv"
1112
"strings"
13+
"time"
1214

1315
"github.com/golang/glog"
1416
"github.com/julienschmidt/httprouter"
@@ -20,6 +22,14 @@ import (
2022
"github.com/livepeer/go-api-client"
2123
)
2224

25+
const (
26+
streamSourceRetries = 20
27+
streamSourceRetryInterval = 3 * time.Second
28+
lockPullLeaseTimeout = 3 * time.Minute
29+
)
30+
31+
var errLockPull = errors.New("failed to lock pull")
32+
2333
type GeolocationHandlersCollection struct {
2434
Balancer balancer.Balancer
2535
Cluster cluster.Cluster
@@ -150,31 +160,52 @@ func (c *GeolocationHandlersCollection) HandleStreamSource(ctx context.Context,
150160

151161
latStr := fmt.Sprintf("%f", lat)
152162
lonStr := fmt.Sprintf("%f", lon)
153-
dtscURL, err := c.Balancer.MistUtilLoadSource(context.Background(), payload.StreamName, latStr, lonStr)
154-
if err != nil {
155-
glog.Errorf("error querying mist for STREAM_SOURCE: %s", err)
163+
for i := 0; i < streamSourceRetries; i++ {
164+
dtscURL, err := c.Balancer.MistUtilLoadSource(context.Background(), payload.StreamName, latStr, lonStr)
165+
if err == nil {
166+
return c.resolveReplicatedStream(dtscURL, payload.StreamName)
167+
}
156168

157-
playbackID := payload.StreamName
158-
parts := strings.Split(playbackID, "+")
159-
if len(parts) == 2 {
160-
playbackID = parts[1] // take the playbackID after the prefix e.g. 'video+'
169+
glog.Errorf("error querying mist for STREAM_SOURCE: %s", err)
170+
pullURL, err := c.getStreamPull(playbackIdFor(payload.StreamName))
171+
if err == nil {
172+
if pullURL == "" {
173+
// not a stream pull
174+
return "push://", nil
175+
} else {
176+
// start stream pull
177+
glog.Infof("replying to Mist STREAM_SOURCE with stream pull request=%s response=%s", payload.StreamName, pullURL)
178+
return pullURL, nil
179+
}
161180
}
162-
pullURL, err := c.getStreamPull(playbackID)
163-
if err != nil {
181+
if !errors.Is(err, errLockPull) {
182+
// stream pull failed for unknown reason
164183
glog.Errorf("getStreamPull failed for %s: %s", payload.StreamName, err)
165-
} else if pullURL != "" {
166-
glog.Infof("replying to Mist STREAM_SOURCE with stream pull request=%s response=%s", payload.StreamName, pullURL)
167-
return pullURL, nil
184+
return "push://", nil
168185
}
186+
// stream pull failed, because another node started to pull at the same time
187+
glog.Warningf("another node is currently pulling the stream, waiting %v and retrying", streamSourceRetryInterval)
188+
time.Sleep(streamSourceRetryInterval)
189+
}
190+
return "push://", nil
191+
}
169192

170-
return "push://", nil
193+
func playbackIdFor(streamName string) string {
194+
res := streamName
195+
parts := strings.Split(res, "+")
196+
if len(parts) == 2 {
197+
res = parts[1] // take the playbackID after the prefix e.g. 'video+'
171198
}
199+
return res
200+
}
201+
202+
func (c *GeolocationHandlersCollection) resolveReplicatedStream(dtscURL string, streamName string) (string, error) {
172203
outURL, err := c.Cluster.ResolveNodeURL(dtscURL)
173204
if err != nil {
174205
glog.Errorf("error finding STREAM_SOURCE: %s", err)
175206
return "push://", nil
176207
}
177-
glog.V(7).Infof("replying to Mist STREAM_SOURCE request=%s response=%s", payload.StreamName, outURL)
208+
glog.V(7).Infof("replying to Mist STREAM_SOURCE request=%s response=%s", streamName, outURL)
178209
return outURL, nil
179210
}
180211

@@ -200,6 +231,10 @@ func (c *GeolocationHandlersCollection) getStreamPull(playbackID string) (string
200231
return "", nil
201232
}
202233

234+
if err := c.Lapi.LockPull(stream.ID, lockPullLeaseTimeout); err != nil {
235+
return "", errLockPull
236+
}
237+
203238
if len(stream.Pull.Headers) == 0 {
204239
return stream.Pull.Source, nil
205240
}

0 commit comments

Comments
 (0)