add support for recovery section in event streams (#2421)
This commit is contained in:
parent
102a22e486
commit
e03fdaaa51
|
|
@ -247,16 +247,18 @@ type ConnectionPooler struct {
|
||||||
|
|
||||||
// Stream defines properties for creating FabricEventStream resources
|
// Stream defines properties for creating FabricEventStream resources
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
ApplicationId string `json:"applicationId"`
|
ApplicationId string `json:"applicationId"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
Tables map[string]StreamTable `json:"tables"`
|
Tables map[string]StreamTable `json:"tables"`
|
||||||
Filter map[string]*string `json:"filter,omitempty"`
|
Filter map[string]*string `json:"filter,omitempty"`
|
||||||
BatchSize *uint32 `json:"batchSize,omitempty"`
|
BatchSize *uint32 `json:"batchSize,omitempty"`
|
||||||
|
EnableRecovery *bool `json:"enableRecovery,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StreamTable defines properties of outbox tables for FabricEventStreams
|
// StreamTable defines properties of outbox tables for FabricEventStreams
|
||||||
type StreamTable struct {
|
type StreamTable struct {
|
||||||
EventType string `json:"eventType"`
|
EventType string `json:"eventType"`
|
||||||
IdColumn *string `json:"idColumn,omitempty"`
|
RecoveryEventType string `json:"recoveryEventType"`
|
||||||
PayloadColumn *string `json:"payloadColumn,omitempty"`
|
IdColumn *string `json:"idColumn,omitempty"`
|
||||||
|
PayloadColumn *string `json:"payloadColumn,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1281,6 +1281,11 @@ func (in *Stream) DeepCopyInto(out *Stream) {
|
||||||
*out = new(uint32)
|
*out = new(uint32)
|
||||||
**out = **in
|
**out = **in
|
||||||
}
|
}
|
||||||
|
if in.EnableRecovery != nil {
|
||||||
|
in, out := &in.EnableRecovery, &out.EnableRecovery
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,10 @@ type FabricEventStreamList struct {
|
||||||
|
|
||||||
// EventStream defines the source, flow and sink of the event stream
|
// EventStream defines the source, flow and sink of the event stream
|
||||||
type EventStream struct {
|
type EventStream struct {
|
||||||
EventStreamFlow EventStreamFlow `json:"flow"`
|
EventStreamFlow EventStreamFlow `json:"flow"`
|
||||||
EventStreamSink EventStreamSink `json:"sink"`
|
EventStreamSink EventStreamSink `json:"sink"`
|
||||||
EventStreamSource EventStreamSource `json:"source"`
|
EventStreamSource EventStreamSource `json:"source"`
|
||||||
|
EventStreamRecovery EventStreamRecovery `json:"recovery"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventStreamFlow defines the flow characteristics of the event stream
|
// EventStreamFlow defines the flow characteristics of the event stream
|
||||||
|
|
@ -51,6 +52,12 @@ type EventStreamSink struct {
|
||||||
MaxBatchSize *uint32 `json:"maxBatchSize,omitempty"`
|
MaxBatchSize *uint32 `json:"maxBatchSize,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EventStreamRecovery defines the target of dead letter queue
|
||||||
|
type EventStreamRecovery struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Sink *EventStreamSink `json:"sink"`
|
||||||
|
}
|
||||||
|
|
||||||
// EventStreamSource defines the source of the event stream and connection for FES operator
|
// EventStreamSource defines the source of the event stream and connection for FES operator
|
||||||
type EventStreamSource struct {
|
type EventStreamSource struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ import (
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Connection) DeepCopyInto(out *Connection) {
|
func (in *Connection) DeepCopyInto(out *Connection) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.PublicationName != nil {
|
||||||
|
in, out := &in.PublicationName, &out.PublicationName
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
in.DBAuth.DeepCopyInto(&out.DBAuth)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +71,10 @@ func (in *DBAuth) DeepCopy() *DBAuth {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *EventStream) DeepCopyInto(out *EventStream) {
|
func (in *EventStream) DeepCopyInto(out *EventStream) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
in.EventStreamFlow.DeepCopyInto(&out.EventStreamFlow)
|
||||||
|
in.EventStreamRecovery.DeepCopyInto(&out.EventStreamRecovery)
|
||||||
|
in.EventStreamSink.DeepCopyInto(&out.EventStreamSink)
|
||||||
|
in.EventStreamSource.DeepCopyInto(&out.EventStreamSource)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,6 +91,11 @@ func (in *EventStream) DeepCopy() *EventStream {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *EventStreamFlow) DeepCopyInto(out *EventStreamFlow) {
|
func (in *EventStreamFlow) DeepCopyInto(out *EventStreamFlow) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.PayloadColumn != nil {
|
||||||
|
in, out := &in.PayloadColumn, &out.PayloadColumn
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,9 +109,35 @@ func (in *EventStreamFlow) DeepCopy() *EventStreamFlow {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *EventStreamRecovery) DeepCopyInto(out *EventStreamRecovery) {
|
||||||
|
*out = *in
|
||||||
|
if in.Sink != nil {
|
||||||
|
in, out := &in.Sink, &out.Sink
|
||||||
|
*out = new(EventStreamSink)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *EventStreamRecovery) DeepCopy() *EventStreamRecovery {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(EventStreamRecovery)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *EventStreamSink) DeepCopyInto(out *EventStreamSink) {
|
func (in *EventStreamSink) DeepCopyInto(out *EventStreamSink) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.MaxBatchSize != nil {
|
||||||
|
in, out := &in.MaxBatchSize, &out.MaxBatchSize
|
||||||
|
*out = new(uint32)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +154,13 @@ func (in *EventStreamSink) DeepCopy() *EventStreamSink {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *EventStreamSource) DeepCopyInto(out *EventStreamSource) {
|
func (in *EventStreamSource) DeepCopyInto(out *EventStreamSource) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
in.Connection.DeepCopyInto(&out.Connection)
|
||||||
|
if in.Filter != nil {
|
||||||
|
in, out := &in.Filter, &out.Filter
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
|
in.EventStreamTable.DeepCopyInto(&out.EventStreamTable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,6 +177,11 @@ func (in *EventStreamSource) DeepCopy() *EventStreamSource {
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *EventStreamTable) DeepCopyInto(out *EventStreamTable) {
|
func (in *EventStreamTable) DeepCopyInto(out *EventStreamTable) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.IDColumn != nil {
|
||||||
|
in, out := &in.IDColumn, &out.IDColumn
|
||||||
|
*out = new(string)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,13 @@ func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEvent
|
||||||
streamSource := c.getEventStreamSource(stream, tableName, table.IdColumn)
|
streamSource := c.getEventStreamSource(stream, tableName, table.IdColumn)
|
||||||
streamFlow := getEventStreamFlow(stream, table.PayloadColumn)
|
streamFlow := getEventStreamFlow(stream, table.PayloadColumn)
|
||||||
streamSink := getEventStreamSink(stream, table.EventType)
|
streamSink := getEventStreamSink(stream, table.EventType)
|
||||||
|
streamRecovery := getEventStreamRecovery(stream, table.RecoveryEventType, table.EventType)
|
||||||
|
|
||||||
eventStreams = append(eventStreams, zalandov1.EventStream{
|
eventStreams = append(eventStreams, zalandov1.EventStream{
|
||||||
EventStreamFlow: streamFlow,
|
EventStreamFlow: streamFlow,
|
||||||
EventStreamSink: streamSink,
|
EventStreamRecovery: streamRecovery,
|
||||||
EventStreamSource: streamSource})
|
EventStreamSink: streamSink,
|
||||||
|
EventStreamSource: streamSource})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,6 +206,28 @@ func getEventStreamSink(stream acidv1.Stream, eventType string) zalandov1.EventS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getEventStreamRecovery(stream acidv1.Stream, recoveryEventType, eventType string) zalandov1.EventStreamRecovery {
|
||||||
|
if (stream.EnableRecovery != nil && !*stream.EnableRecovery) ||
|
||||||
|
(stream.EnableRecovery == nil && recoveryEventType == "") {
|
||||||
|
return zalandov1.EventStreamRecovery{
|
||||||
|
Type: constants.EventStreamRecoveryNoneType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stream.EnableRecovery != nil && *stream.EnableRecovery && recoveryEventType == "" {
|
||||||
|
recoveryEventType = fmt.Sprintf("%s-%s", eventType, constants.EventStreamRecoverySuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
return zalandov1.EventStreamRecovery{
|
||||||
|
Type: constants.EventStreamRecoveryDLQType,
|
||||||
|
Sink: &zalandov1.EventStreamSink{
|
||||||
|
Type: constants.EventStreamSinkNakadiType,
|
||||||
|
EventType: recoveryEventType,
|
||||||
|
MaxBatchSize: stream.BatchSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getTableSchema(fullTableName string) (tableName, schemaName string) {
|
func getTableSchema(fullTableName string) (tableName, schemaName string) {
|
||||||
schemaName = "public"
|
schemaName = "public"
|
||||||
tableName = fullTableName
|
tableName = fullTableName
|
||||||
|
|
@ -381,7 +405,8 @@ func sameStreams(curEventStreams, newEventStreams []zalandov1.EventStream) (matc
|
||||||
for _, curStream := range curEventStreams {
|
for _, curStream := range curEventStreams {
|
||||||
if reflect.DeepEqual(newStream.EventStreamSource, curStream.EventStreamSource) &&
|
if reflect.DeepEqual(newStream.EventStreamSource, curStream.EventStreamSource) &&
|
||||||
reflect.DeepEqual(newStream.EventStreamFlow, curStream.EventStreamFlow) &&
|
reflect.DeepEqual(newStream.EventStreamFlow, curStream.EventStreamFlow) &&
|
||||||
reflect.DeepEqual(newStream.EventStreamSink, curStream.EventStreamSink) {
|
reflect.DeepEqual(newStream.EventStreamSink, curStream.EventStreamSink) &&
|
||||||
|
reflect.DeepEqual(newStream.EventStreamRecovery, curStream.EventStreamRecovery) {
|
||||||
match = true
|
match = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,11 @@ var (
|
||||||
PayloadColumn: k8sutil.StringToPointer("b_payload"),
|
PayloadColumn: k8sutil.StringToPointer("b_payload"),
|
||||||
},
|
},
|
||||||
"data.foobar": acidv1.StreamTable{
|
"data.foobar": acidv1.StreamTable{
|
||||||
EventType: "stream-type-b",
|
EventType: "stream-type-b",
|
||||||
|
RecoveryEventType: "stream-type-b-dlq",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
EnableRecovery: util.True(),
|
||||||
Filter: map[string]*string{
|
Filter: map[string]*string{
|
||||||
"data.bar": k8sutil.StringToPointer("[?(@.source.txId > 500 && @.source.lsn > 123456)]"),
|
"data.bar": k8sutil.StringToPointer("[?(@.source.txId > 500 && @.source.lsn > 123456)]"),
|
||||||
},
|
},
|
||||||
|
|
@ -106,6 +108,14 @@ var (
|
||||||
PayloadColumn: k8sutil.StringToPointer("b_payload"),
|
PayloadColumn: k8sutil.StringToPointer("b_payload"),
|
||||||
Type: constants.EventStreamFlowPgGenericType,
|
Type: constants.EventStreamFlowPgGenericType,
|
||||||
},
|
},
|
||||||
|
EventStreamRecovery: zalandov1.EventStreamRecovery{
|
||||||
|
Type: constants.EventStreamRecoveryDLQType,
|
||||||
|
Sink: &zalandov1.EventStreamSink{
|
||||||
|
EventType: fmt.Sprintf("%s-%s", "stream-type-a", constants.EventStreamRecoverySuffix),
|
||||||
|
MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)),
|
||||||
|
Type: constants.EventStreamSinkNakadiType,
|
||||||
|
},
|
||||||
|
},
|
||||||
EventStreamSink: zalandov1.EventStreamSink{
|
EventStreamSink: zalandov1.EventStreamSink{
|
||||||
EventType: "stream-type-a",
|
EventType: "stream-type-a",
|
||||||
MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)),
|
MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)),
|
||||||
|
|
@ -136,6 +146,14 @@ var (
|
||||||
EventStreamFlow: zalandov1.EventStreamFlow{
|
EventStreamFlow: zalandov1.EventStreamFlow{
|
||||||
Type: constants.EventStreamFlowPgGenericType,
|
Type: constants.EventStreamFlowPgGenericType,
|
||||||
},
|
},
|
||||||
|
EventStreamRecovery: zalandov1.EventStreamRecovery{
|
||||||
|
Type: constants.EventStreamRecoveryDLQType,
|
||||||
|
Sink: &zalandov1.EventStreamSink{
|
||||||
|
EventType: "stream-type-b-dlq",
|
||||||
|
MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)),
|
||||||
|
Type: constants.EventStreamSinkNakadiType,
|
||||||
|
},
|
||||||
|
},
|
||||||
EventStreamSink: zalandov1.EventStreamSink{
|
EventStreamSink: zalandov1.EventStreamSink{
|
||||||
EventType: "stream-type-b",
|
EventType: "stream-type-b",
|
||||||
MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)),
|
MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)),
|
||||||
|
|
@ -251,7 +269,8 @@ func TestSameStreams(t *testing.T) {
|
||||||
testName := "TestSameStreams"
|
testName := "TestSameStreams"
|
||||||
|
|
||||||
stream1 := zalandov1.EventStream{
|
stream1 := zalandov1.EventStream{
|
||||||
EventStreamFlow: zalandov1.EventStreamFlow{},
|
EventStreamFlow: zalandov1.EventStreamFlow{},
|
||||||
|
EventStreamRecovery: zalandov1.EventStreamRecovery{},
|
||||||
EventStreamSink: zalandov1.EventStreamSink{
|
EventStreamSink: zalandov1.EventStreamSink{
|
||||||
EventType: "stream-type-a",
|
EventType: "stream-type-a",
|
||||||
},
|
},
|
||||||
|
|
@ -263,7 +282,8 @@ func TestSameStreams(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stream2 := zalandov1.EventStream{
|
stream2 := zalandov1.EventStream{
|
||||||
EventStreamFlow: zalandov1.EventStreamFlow{},
|
EventStreamFlow: zalandov1.EventStreamFlow{},
|
||||||
|
EventStreamRecovery: zalandov1.EventStreamRecovery{},
|
||||||
EventStreamSink: zalandov1.EventStreamSink{
|
EventStreamSink: zalandov1.EventStreamSink{
|
||||||
EventType: "stream-type-b",
|
EventType: "stream-type-b",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,7 @@ const (
|
||||||
EventStreamSourceAuthType = "DatabaseAuthenticationSecret"
|
EventStreamSourceAuthType = "DatabaseAuthenticationSecret"
|
||||||
EventStreamFlowPgGenericType = "PostgresWalToGenericNakadiEvent"
|
EventStreamFlowPgGenericType = "PostgresWalToGenericNakadiEvent"
|
||||||
EventStreamSinkNakadiType = "Nakadi"
|
EventStreamSinkNakadiType = "Nakadi"
|
||||||
|
EventStreamRecoveryNoneType = "None"
|
||||||
|
EventStreamRecoveryDLQType = "DeadLetter"
|
||||||
|
EventStreamRecoverySuffix = "dead-letter-queue"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue