You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by lo...@apache.org on 2023/12/08 18:36:32 UTC
(beam) branch master updated: [#29605] Go SDK: Eagerly create timer writers to ensure is_last sent, minmize lock contention. (#29607)
This is an automated email from the ASF dual-hosted git repository.
lostluck pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new e19ace02a28 [#29605] Go SDK: Eagerly create timer writers to ensure is_last sent, minmize lock contention. (#29607)
e19ace02a28 is described below
commit e19ace02a28f4fa19ca3db7f0dc43e9c4f4ab3c1
Author: Robert Burke <lo...@users.noreply.github.com>
AuthorDate: Fri Dec 8 10:36:25 2023 -0800
[#29605] Go SDK: Eagerly create timer writers to ensure is_last sent, minmize lock contention. (#29607)
---
sdks/go/examples/timer_wordcap/wordcap.go | 1 -
sdks/go/pkg/beam/core/runtime/harness/datamgr.go | 23 ++++++++++++++++++++++
.../pkg/beam/core/runtime/harness/datamgr_test.go | 12 +++++++++++
3 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/sdks/go/examples/timer_wordcap/wordcap.go b/sdks/go/examples/timer_wordcap/wordcap.go
index db64c10eb4d..01f87edc59b 100644
--- a/sdks/go/examples/timer_wordcap/wordcap.go
+++ b/sdks/go/examples/timer_wordcap/wordcap.go
@@ -118,7 +118,6 @@ func (s *Stateful) OnTimer(ctx context.Context, ts beam.EventTime, sp state.Prov
// Clean up the state that has been evicted.
s.ElementBag.Clear(sp)
s.MinTime.Clear(sp)
- s.OutputState.ClearTag(tp, tag) // Clean up the fired timer tag. (Temporary workaround for a runner bug.)
}
}
}
diff --git a/sdks/go/pkg/beam/core/runtime/harness/datamgr.go b/sdks/go/pkg/beam/core/runtime/harness/datamgr.go
index ed57e3eca59..0f2de99dd2a 100644
--- a/sdks/go/pkg/beam/core/runtime/harness/datamgr.go
+++ b/sdks/go/pkg/beam/core/runtime/harness/datamgr.go
@@ -386,14 +386,31 @@ func (c *DataChannel) read(ctx context.Context) {
return
}
+ // Consolidating required timer writer creation to a optional single lock section.
+ type seenTimers struct {
+ InstID instructionID
+ PTransformID, FamilyID string
+ }
+ neededTimers := map[seenTimers]struct{}{}
+
// Each message may contain segments for multiple streams, so we
// must treat each segment in isolation. We maintain a local cache
// to reduce lock contention.
iterateElements(c, cache, &seenLast, msg.GetTimers(),
func(elm *fnpb.Elements_Timers) exec.Elements {
+ neededTimers[seenTimers{InstID: instructionID(elm.GetInstructionId()), PTransformID: elm.GetTransformId(), FamilyID: elm.GetTimerFamilyId()}] = struct{}{}
return exec.Elements{Timers: elm.GetTimers(), PtransformID: elm.GetTransformId(), TimerFamilyID: elm.GetTimerFamilyId()}
})
+ // Creating a writer is necessary to ensure a "is_last" signal is returned for timers that aren't set.
+ if len(neededTimers) > 0 {
+ c.mu.Lock()
+ for key := range neededTimers {
+ c.makeTimerWriterLocked(ctx, clientID{ptransformID: key.PTransformID, instID: key.InstID}, key.FamilyID)
+ }
+ c.mu.Unlock()
+ }
+
iterateElements(c, cache, &seenLast, msg.GetData(),
func(elm *fnpb.Elements_Data) exec.Elements {
return exec.Elements{Data: elm.GetData(), PtransformID: elm.GetTransformId()}
@@ -629,7 +646,13 @@ func (w *dataWriter) Write(p []byte) (n int, err error) {
func (c *DataChannel) makeTimerWriter(ctx context.Context, id clientID, family string) *timerWriter {
c.mu.Lock()
defer c.mu.Unlock()
+ return c.makeTimerWriterLocked(ctx, id, family)
+}
+// makeTimerWriterLocked does the work of makeTimerWriter, but doesn't call the lock methods.
+//
+// c.mu must be locked when this is called.
+func (c *DataChannel) makeTimerWriterLocked(ctx context.Context, id clientID, family string) *timerWriter {
var m map[timerKey]*timerWriter
var ok bool
if m, ok = c.timerWriters[id.instID]; !ok {
diff --git a/sdks/go/pkg/beam/core/runtime/harness/datamgr_test.go b/sdks/go/pkg/beam/core/runtime/harness/datamgr_test.go
index c7f8ac5858c..92c4d0a8f8c 100644
--- a/sdks/go/pkg/beam/core/runtime/harness/datamgr_test.go
+++ b/sdks/go/pkg/beam/core/runtime/harness/datamgr_test.go
@@ -389,6 +389,18 @@ func TestElementChan(t *testing.T) {
return elms
},
wantSum: 0, wantCount: 0,
+ }, {
+ name: "SomeTimersAndADataThenReaderThenCleanup",
+ sequenceFn: func(ctx context.Context, t *testing.T, client *fakeChanClient, c *DataChannel) <-chan exec.Elements {
+ client.Send(&fnpb.Elements{
+ Timers: []*fnpb.Elements_Timers{timerElm(1, false), timerElm(2, true)},
+ Data: []*fnpb.Elements_Data{dataElm(3, true)},
+ })
+ elms := openChan(ctx, t, c, timerID)
+ c.removeInstruction(instID)
+ return elms
+ },
+ wantSum: 6, wantCount: 3,
},
}
for _, test := range tests {