-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ingest stage to provide synthetic workload for benchmarks #395
Changes from 7 commits
0ad41eb
0cd4f57
a775571
f16ee7f
a82b12d
032255f
b00857b
1db643a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Copyright (C) 2023 IBM, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package api | ||
|
||
type IngestSynthetic struct { | ||
Connections int `yaml:"connections,omitempty" json:"connections,omitempty" doc:"number of connections to maintain"` | ||
BatchMaxLen int `yaml:"batchMaxLen,omitempty" json:"batchMaxLen,omitempty" doc:"the number of accumulated flows before being forwarded for processing"` | ||
FlowLogsPerMin int `yaml:"flowLogsPerMin,omitempty" json:"flowLogsPerMin,omitempty" doc:"the number of flow logs to send per minute"` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* Copyright (C) 2023 IBM, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package ingest | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/netobserv/flowlogs-pipeline/pkg/api" | ||
"github.com/netobserv/flowlogs-pipeline/pkg/config" | ||
"github.com/netobserv/flowlogs-pipeline/pkg/operational" | ||
"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/utils" | ||
"github.com/prometheus/client_golang/prometheus" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
type IngestSynthetic struct { | ||
params api.IngestSynthetic | ||
exitChan <-chan struct{} | ||
metricsProcessed prometheus.Counter | ||
} | ||
|
||
const ( | ||
defaultConnections = 100 | ||
defaultBatchLen = 10 | ||
defaultFlowLogsPerMin = 2000 | ||
) | ||
|
||
var ( | ||
flowLogsProcessed = operational.DefineMetric( | ||
"ingest_synthetic_flows_processed", | ||
"Number of flow logs processed", | ||
operational.TypeCounter, | ||
"stage", | ||
) | ||
) | ||
|
||
// Ingest generates flow logs according to provided parameters | ||
func (ingestS *IngestSynthetic) Ingest(out chan<- config.GenericMap) { | ||
log.Debugf("entering IngestSynthetic Ingest, params = %v", ingestS.params) | ||
// get a list of flow log entries, one per desired connection | ||
// these flow logs will be sent again and again to simulate ongoing traffic on those connections | ||
flowLogs := utils.GenerateConnectionFlowEntries(ingestS.params.Connections) | ||
nLogs := len(flowLogs) | ||
next := 0 | ||
|
||
// compute time interval between batches; divide BatchMaxLen by FlowLogsPerMin and adjust the types | ||
ticker := time.NewTicker(time.Duration(int(time.Minute*time.Duration(ingestS.params.BatchMaxLen)) / ingestS.params.FlowLogsPerMin)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The computation of the interval between batches looks complex to me.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added a comment |
||
|
||
// loop forever | ||
// on each iteration send BatchMaxLen flow logs from the array of flow logs. | ||
// if necessary, send the contents of the flowLogs array multiple times (in sub-batches) to fill the number of BatchMaxLen flow logs needed | ||
for { | ||
select { | ||
case <-ingestS.exitChan: | ||
log.Debugf("exiting IngestSynthetic because of signal") | ||
return | ||
case <-ticker.C: | ||
// flowsLeft designates the number out of BatchMaxLen that must still be sent in this batch | ||
// next designates the next flow log entry to be sent | ||
// remainder designates how many flow logs remain in the flowLogs array that can be sent on the next sub-batch. | ||
flowsLeft := ingestS.params.BatchMaxLen | ||
log.Debugf("flowsLeft = %d", flowsLeft) | ||
subBatchLen := flowsLeft | ||
for flowsLeft > 0 { | ||
remainder := nLogs - next | ||
if subBatchLen > remainder { | ||
subBatchLen = remainder | ||
} | ||
log.Debugf("flowsLeft = %d, remainder = %d, subBatchLen = %d", flowsLeft, remainder, subBatchLen) | ||
subBatch := flowLogs[next : next+subBatchLen] | ||
ingestS.sendBatch(subBatch, out) | ||
ingestS.metricsProcessed.Add(float64(subBatchLen)) | ||
flowsLeft -= subBatchLen | ||
next += subBatchLen | ||
if subBatchLen == remainder { | ||
next = 0 | ||
subBatchLen = flowsLeft | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I explored a different approach to achieve the goal of this loop: Please let me know if it is indeed equivalent and if you think it is clearer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. your suggestion is much simpler and cleaner. Done. |
||
} | ||
} | ||
} | ||
|
||
func (ingestS *IngestSynthetic) sendBatch(flows []config.GenericMap, out chan<- config.GenericMap) { | ||
for _, flow := range flows { | ||
out <- flow | ||
} | ||
} | ||
|
||
// NewIngestSynthetic create a new ingester | ||
func NewIngestSynthetic(opMetrics *operational.Metrics, params config.StageParam) (Ingester, error) { | ||
log.Debugf("entering NewIngestSynthetic") | ||
confIngestSynthetic := api.IngestSynthetic{} | ||
if params.Ingest != nil || params.Ingest.Synthetic != nil { | ||
confIngestSynthetic = *params.Ingest.Synthetic | ||
} | ||
if confIngestSynthetic.Connections == 0 { | ||
confIngestSynthetic.Connections = defaultConnections | ||
} | ||
if confIngestSynthetic.FlowLogsPerMin == 0 { | ||
confIngestSynthetic.FlowLogsPerMin = defaultFlowLogsPerMin | ||
} | ||
if confIngestSynthetic.BatchMaxLen == 0 { | ||
confIngestSynthetic.BatchMaxLen = defaultBatchLen | ||
} | ||
log.Debugf("params = %v", confIngestSynthetic) | ||
|
||
return &IngestSynthetic{ | ||
params: confIngestSynthetic, | ||
exitChan: utils.ExitChannel(), | ||
metricsProcessed: opMetrics.NewCounter(&flowLogsProcessed, params.Name), | ||
}, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Copyright (C) 2023 IBM, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
package ingest | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/netobserv/flowlogs-pipeline/pkg/api" | ||
"github.com/netobserv/flowlogs-pipeline/pkg/config" | ||
"github.com/netobserv/flowlogs-pipeline/pkg/operational" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestIngestSynthetic(t *testing.T) { | ||
// check default values | ||
params := config.StageParam{ | ||
Ingest: &config.Ingest{ | ||
Type: "synthetic", | ||
Synthetic: &api.IngestSynthetic{}, | ||
}, | ||
} | ||
ingest, err := NewIngestSynthetic(operational.NewMetrics(&config.MetricsSettings{}), params) | ||
syn := ingest.(*IngestSynthetic) | ||
require.NoError(t, err) | ||
require.Equal(t, defaultBatchLen, syn.params.BatchMaxLen) | ||
require.Equal(t, defaultConnections, syn.params.Connections) | ||
require.Equal(t, defaultFlowLogsPerMin, syn.params.FlowLogsPerMin) | ||
|
||
batchMaxLen := 3 | ||
connections := 20 | ||
flowLogsPerMin := 1000 | ||
synthetic := api.IngestSynthetic{ | ||
BatchMaxLen: batchMaxLen, | ||
Connections: connections, | ||
FlowLogsPerMin: flowLogsPerMin, | ||
} | ||
params = config.StageParam{ | ||
Ingest: &config.Ingest{ | ||
Type: "synthetic", | ||
Synthetic: &synthetic, | ||
}, | ||
} | ||
ingest, err = NewIngestSynthetic(operational.NewMetrics(&config.MetricsSettings{}), params) | ||
syn = ingest.(*IngestSynthetic) | ||
require.NoError(t, err) | ||
require.Equal(t, batchMaxLen, syn.params.BatchMaxLen) | ||
require.Equal(t, connections, syn.params.Connections) | ||
require.Equal(t, flowLogsPerMin, syn.params.FlowLogsPerMin) | ||
|
||
// run the Ingest method in a separate thread | ||
ingestOutput := make(chan config.GenericMap) | ||
go syn.Ingest(ingestOutput) | ||
|
||
type connection struct { | ||
srcAddr string | ||
dstAddr string | ||
srcPort int | ||
dstPort int | ||
protocol int | ||
} | ||
|
||
// Start collecting flows from the ingester and ensure we have the specified number of distinct connections | ||
connectionMap := make(map[connection]int) | ||
for i := 0; i < (3 * connections); i++ { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the hardcoded 3 the batchMaxLen? or just an arbitrary number to make sure we have multiple flow logs per connection? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just to have (many) more flow logs than connections, and to verify that we accumulate the proper number of connections with multiple flow logs per connection. |
||
flowEntry := <-ingestOutput | ||
conn := connection{ | ||
srcAddr: flowEntry["SrcAddr"].(string), | ||
dstAddr: flowEntry["DstAddr"].(string), | ||
srcPort: flowEntry["SrcPort"].(int), | ||
dstPort: flowEntry["DstPort"].(int), | ||
protocol: flowEntry["Proto"].(int), | ||
} | ||
connectionMap[conn]++ | ||
} | ||
require.Equal(t, connections, len(connectionMap)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename
metricsProcessed
here as well