fix: data race by summary

This commit is contained in:
debugtalk
2022-04-16 13:47:59 +08:00
parent 3424b51e0e
commit 19479ea4bd
4 changed files with 99 additions and 69 deletions

View File

@@ -72,15 +72,16 @@ func (b *HRPBoomer) Quit() {
} }
func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rendezvous) *boomer.Task { func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rendezvous) *boomer.Task {
// init session runner for testcase // init runner for testcase
sessionRunner, err := b.hrpRunner.NewSessionRunner(testcase) // this runner is shared by multiple session runners
caseRunner, err := b.hrpRunner.newCaseRunner(testcase)
if err != nil { if err != nil {
log.Error().Err(err).Msg("failed to create session runner") log.Error().Err(err).Msg("failed to create runner")
os.Exit(1) os.Exit(1)
} }
if sessionRunner.parser.plugin != nil { if caseRunner.parser.plugin != nil {
b.pluginsMutex.Lock() b.pluginsMutex.Lock()
b.plugins = append(b.plugins, sessionRunner.parser.plugin) b.plugins = append(b.plugins, caseRunner.parser.plugin)
b.pluginsMutex.Unlock() b.pluginsMutex.Unlock()
} }
@@ -93,7 +94,7 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
}() }()
// set paramters mode for load testing // set paramters mode for load testing
parametersIterator := sessionRunner.parametersIterator parametersIterator := caseRunner.parametersIterator
parametersIterator.SetUnlimitedMode() parametersIterator.SetUnlimitedMode()
return &boomer.Task{ return &boomer.Task{
@@ -103,11 +104,13 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend
testcaseSuccess := true // flag whole testcase result testcaseSuccess := true // flag whole testcase result
transactionSuccess := true // flag current transaction result transactionSuccess := true // flag current transaction result
// init session runner
sessionRunner := caseRunner.newSession()
if parametersIterator.HasNext() { if parametersIterator.HasNext() {
sessionRunner.updateConfigVariables(parametersIterator.Next()) sessionRunner.updateConfigVariables(parametersIterator.Next())
} }
sessionRunner.resetSession()
startTime := time.Now() startTime := time.Now()
for _, step := range testcase.TestSteps { for _, step := range testcase.TestSteps {
stepResult, err := step.Run(sessionRunner) stepResult, err := step.Run(sessionRunner)

View File

@@ -353,7 +353,7 @@ func TestInitParametersIteratorContent(t *testing.T) {
ParametersSetting: nil, ParametersSetting: nil,
}, },
0, 0,
map[string]interface{}(nil), map[string]interface{}{},
}, },
} }
for _, data := range testData { for _, data := range testData {

View File

@@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"golang.org/x/net/http2" "golang.org/x/net/http2"
@@ -208,11 +209,23 @@ func (r *HRPRunner) Run(testcases ...ITestCase) error {
// NewSessionRunner creates a new session runner for testcase. // NewSessionRunner creates a new session runner for testcase.
// each testcase has its own session runner // each testcase has its own session runner
func (r *HRPRunner) NewSessionRunner(testcase *TestCase) (*SessionRunner, error) { func (r *HRPRunner) NewSessionRunner(testcase *TestCase) (*SessionRunner, error) {
runner, err := r.newCaseRunner(testcase)
if err != nil {
return nil, err
}
sessionRunner := &SessionRunner{ sessionRunner := &SessionRunner{
testCaseRunner: runner,
}
sessionRunner.resetSession()
return sessionRunner, nil
}
func (r *HRPRunner) newCaseRunner(testcase *TestCase) (*testCaseRunner, error) {
runner := &testCaseRunner{
testCase: testcase, testCase: testcase,
hrpRunner: r, hrpRunner: r,
parser: newParser(), parser: newParser(),
summary: newSummary(),
} }
// init parser plugin // init parser plugin
@@ -220,12 +233,80 @@ func (r *HRPRunner) NewSessionRunner(testcase *TestCase) (*SessionRunner, error)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "init plugin failed") return nil, errors.Wrap(err, "init plugin failed")
} }
sessionRunner.parser.plugin = plugin runner.parser.plugin = plugin
// parse testcase config // parse testcase config
if err := sessionRunner.parseConfig(); err != nil { if err := runner.parseConfig(); err != nil {
return nil, errors.Wrap(err, "parse testcase config failed") return nil, errors.Wrap(err, "parse testcase config failed")
} }
return sessionRunner, nil return runner, nil
}
type testCaseRunner struct {
testCase *TestCase
hrpRunner *HRPRunner
parser *Parser
parsedConfig *TConfig
parametersIterator *ParametersIterator
}
// parseConfig parses testcase config, stores to parsedConfig.
func (r *testCaseRunner) parseConfig() error {
cfg := r.testCase.Config
r.parsedConfig = &TConfig{}
// deep copy config to avoid data racing
if err := copier.Copy(r.parsedConfig, cfg); err != nil {
log.Error().Err(err).Msg("copy testcase config failed")
return err
}
// parse config variables
parsedVariables, err := r.parser.ParseVariables(cfg.Variables)
if err != nil {
log.Error().Interface("variables", cfg.Variables).Err(err).Msg("parse config variables failed")
return err
}
r.parsedConfig.Variables = parsedVariables
// parse config name
parsedName, err := r.parser.ParseString(cfg.Name, parsedVariables)
if err != nil {
return errors.Wrap(err, "parse config name failed")
}
r.parsedConfig.Name = convertString(parsedName)
// parse config base url
parsedBaseURL, err := r.parser.ParseString(cfg.BaseURL, parsedVariables)
if err != nil {
return errors.Wrap(err, "parse config base url failed")
}
r.parsedConfig.BaseURL = convertString(parsedBaseURL)
// ensure correction of think time config
r.parsedConfig.ThinkTimeSetting.checkThinkTime()
// parse testcase config parameters
parametersIterator, err := initParametersIterator(r.parsedConfig)
if err != nil {
log.Error().Err(err).
Interface("parameters", r.parsedConfig.Parameters).
Interface("parametersSetting", r.parsedConfig.ParametersSetting).
Msg("parse config parameters failed")
return errors.Wrap(err, "parse testcase config parameters failed")
}
r.parametersIterator = parametersIterator
return nil
}
// each boomer task initiates a new session
// in order to avoid data racing
func (r *testCaseRunner) newSession() *SessionRunner {
sessionRunner := &SessionRunner{
testCaseRunner: r,
}
sessionRunner.resetSession()
return sessionRunner
} }

View File

@@ -4,7 +4,6 @@ import (
_ "embed" _ "embed"
"time" "time"
"github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@@ -12,12 +11,8 @@ import (
// SessionRunner is used to run testcase and its steps. // SessionRunner is used to run testcase and its steps.
// each testcase has its own SessionRunner instance and share session variables. // each testcase has its own SessionRunner instance and share session variables.
type SessionRunner struct { type SessionRunner struct {
testCase *TestCase *testCaseRunner
hrpRunner *HRPRunner sessionVariables map[string]interface{}
parser *Parser
parsedConfig *TConfig
parametersIterator *ParametersIterator
sessionVariables map[string]interface{}
// transactions stores transaction timing info. // transactions stores transaction timing info.
// key is transaction name, value is map of transaction type and time, e.g. start time and end time. // key is transaction name, value is map of transaction type and time, e.g. start time and end time.
transactions map[string]map[transactionType]time.Time transactions map[string]map[transactionType]time.Time
@@ -30,6 +25,7 @@ func (r *SessionRunner) resetSession() {
r.sessionVariables = make(map[string]interface{}) r.sessionVariables = make(map[string]interface{})
r.transactions = make(map[string]map[transactionType]time.Time) r.transactions = make(map[string]map[transactionType]time.Time)
r.startTime = time.Now() r.startTime = time.Now()
r.summary = newSummary()
} }
func (r *SessionRunner) GetParser() *Parser { func (r *SessionRunner) GetParser() *Parser {
@@ -122,56 +118,6 @@ func (r *SessionRunner) updateConfigVariables(parameters map[string]interface{})
} }
} }
// parseConfig parses testcase config, stores to parsedConfig.
func (r *SessionRunner) parseConfig() error {
cfg := r.testCase.Config
r.parsedConfig = &TConfig{}
// deep copy config to avoid data racing
if err := copier.Copy(r.parsedConfig, cfg); err != nil {
log.Error().Err(err).Msg("copy testcase config failed")
return err
}
// parse config variables
parsedVariables, err := r.parser.ParseVariables(cfg.Variables)
if err != nil {
log.Error().Interface("variables", cfg.Variables).Err(err).Msg("parse config variables failed")
return err
}
r.parsedConfig.Variables = parsedVariables
// parse config name
parsedName, err := r.parser.ParseString(cfg.Name, parsedVariables)
if err != nil {
return errors.Wrap(err, "parse config name failed")
}
r.parsedConfig.Name = convertString(parsedName)
// parse config base url
parsedBaseURL, err := r.parser.ParseString(cfg.BaseURL, parsedVariables)
if err != nil {
return errors.Wrap(err, "parse config base url failed")
}
r.parsedConfig.BaseURL = convertString(parsedBaseURL)
// ensure correction of think time config
r.parsedConfig.ThinkTimeSetting.checkThinkTime()
// parse testcase config parameters
parametersIterator, err := initParametersIterator(r.parsedConfig)
if err != nil {
log.Error().Err(err).
Interface("parameters", r.parsedConfig.Parameters).
Interface("parametersSetting", r.parsedConfig.ParametersSetting).
Msg("parse config parameters failed")
return errors.Wrap(err, "parse testcase config parameters failed")
}
r.parametersIterator = parametersIterator
return nil
}
func (r *SessionRunner) GetSummary() *TestCaseSummary { func (r *SessionRunner) GetSummary() *TestCaseSummary {
caseSummary := r.summary caseSummary := r.summary
caseSummary.Name = r.parsedConfig.Name caseSummary.Name = r.parsedConfig.Name