|
@@ -15,19 +15,30 @@
|
|
package node
|
|
package node
|
|
|
|
|
|
import (
|
|
import (
|
|
- "bytes"
|
|
|
|
- "encoding/json"
|
|
|
|
"fmt"
|
|
"fmt"
|
|
"github.com/lf-edge/ekuiper/internal/binder/io"
|
|
"github.com/lf-edge/ekuiper/internal/binder/io"
|
|
"github.com/lf-edge/ekuiper/internal/conf"
|
|
"github.com/lf-edge/ekuiper/internal/conf"
|
|
- ct "github.com/lf-edge/ekuiper/internal/template"
|
|
|
|
|
|
+ "github.com/lf-edge/ekuiper/internal/topo/context"
|
|
|
|
+ "github.com/lf-edge/ekuiper/internal/topo/transform"
|
|
"github.com/lf-edge/ekuiper/pkg/api"
|
|
"github.com/lf-edge/ekuiper/pkg/api"
|
|
"github.com/lf-edge/ekuiper/pkg/cast"
|
|
"github.com/lf-edge/ekuiper/pkg/cast"
|
|
|
|
+ "strings"
|
|
"sync"
|
|
"sync"
|
|
- "text/template"
|
|
|
|
"time"
|
|
"time"
|
|
)
|
|
)
|
|
|
|
|
|
|
|
+type SinkConf struct {
|
|
|
|
+ Concurrency int `json:"concurrency"`
|
|
|
|
+ RunAsync bool `json:"runAsync"`
|
|
|
|
+ RetryInterval int `json:"retryInterval"`
|
|
|
|
+ RetryCount int `json:"retryCount"`
|
|
|
|
+ CacheLength int `json:"cacheLength"`
|
|
|
|
+ CacheSaveInterval int `json:"cacheSaveInterval"`
|
|
|
|
+ Omitempty bool `json:"omitIfEmpty"`
|
|
|
|
+ SendSingle bool `json:"sendSingle"`
|
|
|
|
+ DataTemplate string `json:"dataTemplate"`
|
|
|
|
+}
|
|
|
|
+
|
|
type SinkNode struct {
|
|
type SinkNode struct {
|
|
*defaultSinkNode
|
|
*defaultSinkNode
|
|
//static
|
|
//static
|
|
@@ -64,7 +75,7 @@ func NewSinkNode(name string, sinkType string, props map[string]interface{}) *Si
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-//Only for mock source, do not use it in production
|
|
|
|
|
|
+// NewSinkNodeWithSink Only for mock source, do not use it in production
|
|
func NewSinkNodeWithSink(name string, sink api.Sink, props map[string]interface{}) *SinkNode {
|
|
func NewSinkNodeWithSink(name string, sink api.Sink, props map[string]interface{}) *SinkNode {
|
|
return &SinkNode{
|
|
return &SinkNode{
|
|
defaultSinkNode: &defaultSinkNode{
|
|
defaultSinkNode: &defaultSinkNode{
|
|
@@ -89,85 +100,50 @@ func (m *SinkNode) Open(ctx api.StreamContext, result chan<- error) {
|
|
m.tch = make(chan struct{})
|
|
m.tch = make(chan struct{})
|
|
}
|
|
}
|
|
go func() {
|
|
go func() {
|
|
- if c, ok := m.options["concurrency"]; ok {
|
|
|
|
- if t, err := cast.ToInt(c, cast.STRICT); err != nil || t <= 0 {
|
|
|
|
- logger.Warnf("invalid type for concurrency property, should be positive integer but found %t", c)
|
|
|
|
- } else {
|
|
|
|
- m.concurrency = t
|
|
|
|
- }
|
|
|
|
|
|
+ sconf := &SinkConf{
|
|
|
|
+ Concurrency: 1,
|
|
|
|
+ RunAsync: false,
|
|
|
|
+ RetryInterval: 1000,
|
|
|
|
+ RetryCount: 0,
|
|
|
|
+ CacheLength: 1024,
|
|
|
|
+ CacheSaveInterval: 1000,
|
|
|
|
+ Omitempty: false,
|
|
|
|
+ SendSingle: false,
|
|
|
|
+ DataTemplate: "",
|
|
}
|
|
}
|
|
- runAsync := false
|
|
|
|
- if c, ok := m.options["runAsync"]; ok {
|
|
|
|
- if t, ok := c.(bool); !ok {
|
|
|
|
- logger.Warnf("invalid type for runAsync property, should be bool but found %t", c)
|
|
|
|
- } else {
|
|
|
|
- runAsync = t
|
|
|
|
- }
|
|
|
|
|
|
+ err := cast.MapToStruct(m.options, sconf)
|
|
|
|
+ if err != nil {
|
|
|
|
+ result <- fmt.Errorf("read properties %v fail with error: %v", m.options, err)
|
|
|
|
+ return
|
|
}
|
|
}
|
|
- retryInterval := 1000
|
|
|
|
- if c, ok := m.options["retryInterval"]; ok {
|
|
|
|
- if t, err := cast.ToInt(c, cast.STRICT); err != nil || t < 0 {
|
|
|
|
- logger.Warnf("invalid type for retryInterval property, should be positive integer but found %t", c)
|
|
|
|
- } else {
|
|
|
|
- retryInterval = t
|
|
|
|
- }
|
|
|
|
|
|
+ if sconf.Concurrency <= 0 {
|
|
|
|
+ logger.Warnf("invalid type for concurrency property, should be positive integer but found %t", sconf.Concurrency)
|
|
|
|
+ sconf.Concurrency = 1
|
|
}
|
|
}
|
|
- retryCount := 3
|
|
|
|
- if c, ok := m.options["retryCount"]; ok {
|
|
|
|
- if t, err := cast.ToInt(c, cast.STRICT); err != nil || t < 0 {
|
|
|
|
- logger.Warnf("invalid type for retryCount property, should be positive integer but found %t", c)
|
|
|
|
- } else {
|
|
|
|
- retryCount = t
|
|
|
|
- }
|
|
|
|
|
|
+ m.concurrency = sconf.Concurrency
|
|
|
|
+ if sconf.RetryInterval <= 0 {
|
|
|
|
+ logger.Warnf("invalid type for retryInterval property, should be positive integer but found %t", sconf.RetryInterval)
|
|
|
|
+ sconf.RetryInterval = 1000
|
|
}
|
|
}
|
|
- cacheLength := 1024
|
|
|
|
- if c, ok := m.options["cacheLength"]; ok {
|
|
|
|
- if t, err := cast.ToInt(c, cast.STRICT); err != nil || t < 0 {
|
|
|
|
- logger.Warnf("invalid type for cacheLength property, should be positive integer but found %t", c)
|
|
|
|
- } else {
|
|
|
|
- cacheLength = t
|
|
|
|
- }
|
|
|
|
|
|
+ if sconf.RetryCount < 0 {
|
|
|
|
+ logger.Warnf("invalid type for retryCount property, should be positive integer but found %t", sconf.RetryCount)
|
|
|
|
+ sconf.RetryCount = 3
|
|
}
|
|
}
|
|
- cacheSaveInterval := 1000
|
|
|
|
- if c, ok := m.options["cacheSaveInterval"]; ok {
|
|
|
|
- if t, err := cast.ToInt(c, cast.STRICT); err != nil || t < 0 {
|
|
|
|
- logger.Warnf("invalid type for cacheSaveInterval property, should be positive integer but found %t", c)
|
|
|
|
- } else {
|
|
|
|
- cacheSaveInterval = t
|
|
|
|
- }
|
|
|
|
|
|
+ if sconf.CacheLength < 0 {
|
|
|
|
+ logger.Warnf("invalid type for cacheLength property, should be positive integer but found %t", sconf.CacheLength)
|
|
|
|
+ sconf.CacheLength = 1024
|
|
}
|
|
}
|
|
- omitIfEmpty := false
|
|
|
|
- if c, ok := m.options["omitIfEmpty"]; ok {
|
|
|
|
- if t, ok := c.(bool); !ok {
|
|
|
|
- logger.Warnf("invalid type for omitIfEmpty property, should be a bool value 'true/false'.", c)
|
|
|
|
- } else {
|
|
|
|
- omitIfEmpty = t
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- sendSingle := false
|
|
|
|
- if c, ok := m.options["sendSingle"]; ok {
|
|
|
|
- if t, ok := c.(bool); !ok {
|
|
|
|
- logger.Warnf("invalid type for sendSingle property, should be a bool value 'true/false'.", c)
|
|
|
|
- } else {
|
|
|
|
- sendSingle = t
|
|
|
|
- }
|
|
|
|
|
|
+ if sconf.CacheSaveInterval < 0 {
|
|
|
|
+ logger.Warnf("invalid type for cacheSaveInterval property, should be positive integer but found %t", sconf.CacheSaveInterval)
|
|
|
|
+ sconf.CacheSaveInterval = 1000
|
|
}
|
|
}
|
|
- var tp *template.Template = nil
|
|
|
|
- if c, ok := m.options["dataTemplate"]; ok {
|
|
|
|
- if t, ok := c.(string); !ok {
|
|
|
|
- logger.Warnf("invalid type for dateTemplate property, should be a string value.", c)
|
|
|
|
- } else {
|
|
|
|
|
|
|
|
- temp, err := template.New("sink").Funcs(ct.FuncMap).Parse(t)
|
|
|
|
- if err != nil {
|
|
|
|
- msg := fmt.Sprintf("property dataTemplate %v is invalid: %v", t, err)
|
|
|
|
- logger.Warnf(msg)
|
|
|
|
- result <- fmt.Errorf(msg)
|
|
|
|
- return
|
|
|
|
- } else {
|
|
|
|
- tp = temp
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ tf, err := transform.GenTransform(sconf.DataTemplate)
|
|
|
|
+ if err != nil {
|
|
|
|
+ msg := fmt.Sprintf("property dataTemplate %v is invalid: %v", sconf.DataTemplate, err)
|
|
|
|
+ logger.Warnf(msg)
|
|
|
|
+ result <- fmt.Errorf(msg)
|
|
|
|
+ return
|
|
}
|
|
}
|
|
|
|
|
|
m.reset()
|
|
m.reset()
|
|
@@ -210,16 +186,16 @@ func (m *SinkNode) Open(ctx api.StreamContext, result chan<- error) {
|
|
for {
|
|
for {
|
|
select {
|
|
select {
|
|
case data := <-m.input:
|
|
case data := <-m.input:
|
|
- if newdata, processed := m.preprocess(data); processed {
|
|
|
|
|
|
+ if temp, processed := m.preprocess(data); processed {
|
|
break
|
|
break
|
|
} else {
|
|
} else {
|
|
- data = newdata
|
|
|
|
|
|
+ data = temp
|
|
}
|
|
}
|
|
stats.SetBufferLength(int64(len(m.input)))
|
|
stats.SetBufferLength(int64(len(m.input)))
|
|
- if runAsync {
|
|
|
|
- go doCollect(sink, data, stats, omitIfEmpty, sendSingle, tp, ctx)
|
|
|
|
|
|
+ if sconf.RunAsync {
|
|
|
|
+ go doCollect(ctx, sink, data, stats, sconf, tf, nil)
|
|
} else {
|
|
} else {
|
|
- doCollect(sink, data, stats, omitIfEmpty, sendSingle, tp, ctx)
|
|
|
|
|
|
+ doCollect(ctx, sink, data, stats, sconf, tf, nil)
|
|
}
|
|
}
|
|
case <-ctx.Done():
|
|
case <-ctx.Done():
|
|
logger.Infof("sink node %s instance %d done", m.name, instance)
|
|
logger.Infof("sink node %s instance %d done", m.name, instance)
|
|
@@ -235,23 +211,23 @@ func (m *SinkNode) Open(ctx api.StreamContext, result chan<- error) {
|
|
logger.Infof("Creating sink cache")
|
|
logger.Infof("Creating sink cache")
|
|
var cache *Cache
|
|
var cache *Cache
|
|
if m.qos >= api.AtLeastOnce {
|
|
if m.qos >= api.AtLeastOnce {
|
|
- cache = NewCheckpointbasedCache(m.input, cacheLength, m.tch, result, ctx)
|
|
|
|
|
|
+ cache = NewCheckpointbasedCache(m.input, sconf.CacheLength, m.tch, result, ctx)
|
|
} else {
|
|
} else {
|
|
- cache = NewTimebasedCache(m.input, cacheLength, cacheSaveInterval, result, ctx)
|
|
|
|
|
|
+ cache = NewTimebasedCache(m.input, sconf.CacheLength, sconf.CacheSaveInterval, result, ctx)
|
|
}
|
|
}
|
|
for {
|
|
for {
|
|
select {
|
|
select {
|
|
case data := <-cache.Out:
|
|
case data := <-cache.Out:
|
|
- if newdata, processed := m.preprocess(data.data); processed {
|
|
|
|
|
|
+ if temp, processed := m.preprocess(data.data); processed {
|
|
break
|
|
break
|
|
} else {
|
|
} else {
|
|
- data.data = newdata
|
|
|
|
|
|
+ data.data = temp
|
|
}
|
|
}
|
|
stats.SetBufferLength(int64(len(m.input)))
|
|
stats.SetBufferLength(int64(len(m.input)))
|
|
- if runAsync {
|
|
|
|
- go doCollectCacheTuple(sink, data, stats, retryInterval, retryCount, omitIfEmpty, sendSingle, tp, cache.Complete, ctx)
|
|
|
|
|
|
+ if sconf.RunAsync {
|
|
|
|
+ go doCollect(ctx, sink, data, stats, sconf, tf, cache.Complete)
|
|
} else {
|
|
} else {
|
|
- doCollectCacheTuple(sink, data, stats, retryInterval, retryCount, omitIfEmpty, sendSingle, tp, cache.Complete, ctx)
|
|
|
|
|
|
+ doCollect(ctx, sink, data, stats, sconf, tf, cache.Complete)
|
|
}
|
|
}
|
|
case <-ctx.Done():
|
|
case <-ctx.Done():
|
|
logger.Infof("sink node %s instance %d done", m.name, instance)
|
|
logger.Infof("sink node %s instance %d done", m.name, instance)
|
|
@@ -274,131 +250,74 @@ func (m *SinkNode) reset() {
|
|
m.statManagers = nil
|
|
m.statManagers = nil
|
|
}
|
|
}
|
|
|
|
|
|
-func extractInput(v []byte) ([]map[string]interface{}, error) {
|
|
|
|
- var j []map[string]interface{}
|
|
|
|
- if err := json.Unmarshal(v, &j); err != nil {
|
|
|
|
- return nil, fmt.Errorf("fail to decode the input %s as json: %v", v, err)
|
|
|
|
- }
|
|
|
|
- return j, nil
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func doCollect(sink api.Sink, item interface{}, stats StatManager, omitIfEmpty bool, sendSingle bool, tp *template.Template, ctx api.StreamContext) {
|
|
|
|
|
|
+func doCollect(ctx api.StreamContext, sink api.Sink, item interface{}, stats StatManager, sconf *SinkConf, tp transform.TransFunc, signalCh chan<- int) {
|
|
stats.IncTotalRecordsIn()
|
|
stats.IncTotalRecordsIn()
|
|
stats.ProcessTimeStart()
|
|
stats.ProcessTimeStart()
|
|
defer stats.ProcessTimeEnd()
|
|
defer stats.ProcessTimeEnd()
|
|
- logger := ctx.GetLogger()
|
|
|
|
- outdatas := getOutData(stats, ctx, item, omitIfEmpty, sendSingle, tp)
|
|
|
|
-
|
|
|
|
- for _, outdata := range outdatas {
|
|
|
|
- if err := sink.Collect(ctx, outdata); err != nil {
|
|
|
|
- stats.IncTotalExceptions()
|
|
|
|
- logger.Warnf("sink node %s instance %d publish %s error: %v", ctx.GetOpId(), ctx.GetInstanceId(), outdata, err)
|
|
|
|
- } else {
|
|
|
|
- stats.IncTotalRecordsOut()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func getOutData(stats StatManager, ctx api.StreamContext, item interface{}, omitIfEmpty bool, sendSingle bool, tp *template.Template) [][]byte {
|
|
|
|
- logger := ctx.GetLogger()
|
|
|
|
- var outdatas [][]byte
|
|
|
|
|
|
+ var outs []map[string]interface{}
|
|
switch val := item.(type) {
|
|
switch val := item.(type) {
|
|
- case []byte:
|
|
|
|
- if omitIfEmpty && string(val) == "[{}]" {
|
|
|
|
- return nil
|
|
|
|
|
|
+ case error:
|
|
|
|
+ outs = []map[string]interface{}{
|
|
|
|
+ {"error": val.Error()},
|
|
}
|
|
}
|
|
- var (
|
|
|
|
- err error
|
|
|
|
- j []map[string]interface{}
|
|
|
|
- )
|
|
|
|
- if sendSingle || tp != nil {
|
|
|
|
- j, err = extractInput(val)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Warnf("sink node %s instance %d publish %s error: %v", ctx.GetOpId(), ctx.GetInstanceId(), val, err)
|
|
|
|
- stats.IncTotalExceptions()
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- logger.Debugf("receive %d records", len(j))
|
|
|
|
|
|
+ case []map[string]interface{}:
|
|
|
|
+ outs = val
|
|
|
|
+ default:
|
|
|
|
+ outs = []map[string]interface{}{
|
|
|
|
+ {"error": fmt.Sprintf("result is not a string but found %#v", val)},
|
|
}
|
|
}
|
|
- if !sendSingle {
|
|
|
|
- if tp != nil {
|
|
|
|
- var output bytes.Buffer
|
|
|
|
- err := tp.Execute(&output, j)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Warnf("sink node %s instance %d publish %s decode template error: %v", ctx.GetOpId(), ctx.GetInstanceId(), val, err)
|
|
|
|
- stats.IncTotalExceptions()
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- outdatas = append(outdatas, output.Bytes())
|
|
|
|
- } else {
|
|
|
|
- outdatas = [][]byte{val}
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- for _, r := range j {
|
|
|
|
- if tp != nil {
|
|
|
|
- var output bytes.Buffer
|
|
|
|
- err := tp.Execute(&output, r)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Warnf("sink node %s instance %d publish %s decode template error: %v", ctx.GetOpId(), ctx.GetInstanceId(), val, err)
|
|
|
|
- stats.IncTotalExceptions()
|
|
|
|
- return nil
|
|
|
|
- }
|
|
|
|
- outdatas = append(outdatas, output.Bytes())
|
|
|
|
- } else {
|
|
|
|
- if ot, e := json.Marshal(r); e != nil {
|
|
|
|
- logger.Warnf("sink node %s instance %d publish %s marshal error: %v", ctx.GetOpId(), ctx.GetInstanceId(), r, e)
|
|
|
|
- stats.IncTotalExceptions()
|
|
|
|
- return nil
|
|
|
|
- } else {
|
|
|
|
- outdatas = append(outdatas, ot)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
+ if sconf.Omitempty && (item == nil || len(outs) == 0) {
|
|
|
|
+ ctx.GetLogger().Debugf("receive empty in sink")
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if !sconf.SendSingle {
|
|
|
|
+ doCollectData(ctx, sink, outs, stats, sconf, tp, signalCh)
|
|
|
|
+ } else {
|
|
|
|
+ for _, d := range outs {
|
|
|
|
+ doCollectData(ctx, sink, d, stats, sconf, tp, signalCh)
|
|
}
|
|
}
|
|
-
|
|
|
|
- case error:
|
|
|
|
- outdatas = [][]byte{[]byte(fmt.Sprintf(`[{"error":"%s"}]`, val.Error()))}
|
|
|
|
- default:
|
|
|
|
- outdatas = [][]byte{[]byte(fmt.Sprintf(`[{"error":"result is not a string but found %#v"}]`, val))}
|
|
|
|
}
|
|
}
|
|
- return outdatas
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-func doCollectCacheTuple(sink api.Sink, item *CacheTuple, stats StatManager, retryInterval, retryCount int, omitIfEmpty bool, sendSingle bool, tp *template.Template, signalCh chan<- int, ctx api.StreamContext) {
|
|
|
|
- stats.IncTotalRecordsIn()
|
|
|
|
- stats.ProcessTimeStart()
|
|
|
|
- defer stats.ProcessTimeEnd()
|
|
|
|
- logger := ctx.GetLogger()
|
|
|
|
- outdatas := getOutData(stats, ctx, item.data, omitIfEmpty, sendSingle, tp)
|
|
|
|
- for _, outdata := range outdatas {
|
|
|
|
- outerloop:
|
|
|
|
- for {
|
|
|
|
- select {
|
|
|
|
- case <-ctx.Done():
|
|
|
|
- logger.Infof("sink node %s instance %d stops data resending", ctx.GetOpId(), ctx.GetInstanceId())
|
|
|
|
- return
|
|
|
|
- default:
|
|
|
|
- if err := sink.Collect(ctx, outdata); err != nil {
|
|
|
|
- stats.IncTotalExceptions()
|
|
|
|
- logger.Warnf("sink node %s instance %d publish %s error: %v", ctx.GetOpId(), ctx.GetInstanceId(), outdata, err)
|
|
|
|
- if retryInterval > 0 && retryCount > 0 {
|
|
|
|
- retryCount--
|
|
|
|
- time.Sleep(time.Duration(retryInterval) * time.Millisecond)
|
|
|
|
- logger.Debugf("try again")
|
|
|
|
- } else {
|
|
|
|
- break outerloop
|
|
|
|
- }
|
|
|
|
|
|
+// doCollectData outData must be map or []map
|
|
|
|
+func doCollectData(ctx api.StreamContext, sink api.Sink, outData interface{}, stats StatManager, sconf *SinkConf, tf transform.TransFunc, signalCh chan<- int) {
|
|
|
|
+ vCtx := context.WithValue(ctx.(*context.DefaultContext), context.TransKey, &context.TransConfig{
|
|
|
|
+ Data: outData,
|
|
|
|
+ TFunc: tf,
|
|
|
|
+ })
|
|
|
|
+ retries := sconf.RetryCount
|
|
|
|
+ for {
|
|
|
|
+ select {
|
|
|
|
+ case <-ctx.Done():
|
|
|
|
+ ctx.GetLogger().Infof("sink node %s instance %d stops data resending", ctx.GetOpId(), ctx.GetInstanceId())
|
|
|
|
+ return
|
|
|
|
+ default:
|
|
|
|
+ if err := sink.Collect(vCtx, outData); err != nil {
|
|
|
|
+ stats.IncTotalExceptions()
|
|
|
|
+ ctx.GetLogger().Warnf("sink node %s instance %d publish %s error: %v", ctx.GetOpId(), ctx.GetInstanceId(), outData, err)
|
|
|
|
+ if sconf.RetryInterval > 0 && retries > 0 && strings.HasPrefix(err.Error(), "io error") {
|
|
|
|
+ retries--
|
|
|
|
+ time.Sleep(time.Duration(sconf.RetryInterval) * time.Millisecond)
|
|
|
|
+ ctx.GetLogger().Debugf("try again")
|
|
} else {
|
|
} else {
|
|
- logger.Debugf("success")
|
|
|
|
- stats.IncTotalRecordsOut()
|
|
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ ctx.GetLogger().Debugf("success")
|
|
|
|
+ stats.IncTotalRecordsOut()
|
|
|
|
+ if signalCh != nil {
|
|
|
|
+ cacheTuple, ok := outData.(*CacheTuple)
|
|
|
|
+ if !ok {
|
|
|
|
+ ctx.GetLogger().Warnf("got none cache tuple %v, should not happen", outData)
|
|
|
|
+ }
|
|
select {
|
|
select {
|
|
- case signalCh <- item.index:
|
|
|
|
|
|
+ case signalCh <- cacheTuple.index:
|
|
default:
|
|
default:
|
|
- logger.Warnf("sink cache missing response for %d", item.index)
|
|
|
|
|
|
+ ctx.GetLogger().Warnf("sink cache missing response for %d", cacheTuple.index)
|
|
}
|
|
}
|
|
-
|
|
|
|
- break outerloop
|
|
|
|
}
|
|
}
|
|
|
|
+ return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -425,12 +344,12 @@ func getSink(name string, action map[string]interface{}) (api.Sink, error) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-//Override defaultNode
|
|
|
|
|
|
+// AddOutput Override defaultNode
|
|
func (m *SinkNode) AddOutput(_ chan<- interface{}, name string) error {
|
|
func (m *SinkNode) AddOutput(_ chan<- interface{}, name string) error {
|
|
return fmt.Errorf("fail to add output %s, sink %s cannot add output", name, m.name)
|
|
return fmt.Errorf("fail to add output %s, sink %s cannot add output", name, m.name)
|
|
}
|
|
}
|
|
|
|
|
|
-//Override defaultNode
|
|
|
|
|
|
+// Broadcast Override defaultNode
|
|
func (m *SinkNode) Broadcast(_ interface{}) error {
|
|
func (m *SinkNode) Broadcast(_ interface{}) error {
|
|
return fmt.Errorf("sink %s cannot add broadcast", m.name)
|
|
return fmt.Errorf("sink %s cannot add broadcast", m.name)
|
|
}
|
|
}
|
|
@@ -458,7 +377,7 @@ func (m *SinkNode) close(ctx api.StreamContext, logger api.Logger) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-// Only called when checkpoint enabled
|
|
|
|
|
|
+// SaveCache Only called when checkpoint enabled
|
|
func (m *SinkNode) SaveCache() {
|
|
func (m *SinkNode) SaveCache() {
|
|
m.tch <- struct{}{}
|
|
m.tch <- struct{}{}
|
|
}
|
|
}
|