diff --git a/common/constants.go b/common/constants.go index 08c3ddbb868..c121a9e9d5c 100644 --- a/common/constants.go +++ b/common/constants.go @@ -309,3 +309,7 @@ const ( ShardModeHashRing = "hash-ring" ShardModeShardDistributor = "shard-distributor" ) + +const ( + StringSizeOverheadBytes = 16 +) diff --git a/common/definition/workflowIdentifier.go b/common/definition/workflowIdentifier.go index 14cd02a633d..90140d363a9 100644 --- a/common/definition/workflowIdentifier.go +++ b/common/definition/workflowIdentifier.go @@ -20,6 +20,8 @@ package definition +import "github.com/uber/cadence/common" + type ( // WorkflowIdentifier is the combinations which represent a workflow WorkflowIdentifier struct { @@ -29,6 +31,15 @@ type ( } ) +// Size calculates the size in bytes of the WorkflowIdentifier struct. +func (wi *WorkflowIdentifier) Size() uint64 { + // Calculate the size of strings in bytes, we assume that all those fields are using ASCII which is 1 byte per char + size := len(wi.DomainID) + len(wi.WorkflowID) + len(wi.RunID) + // Each string internally holds a reference pointer and a length, which are 8 bytes each + stringOverhead := 3 * common.StringSizeOverheadBytes + return uint64(size + stringOverhead) +} + // NewWorkflowIdentifier create a new WorkflowIdentifier func NewWorkflowIdentifier(domainID string, workflowID string, runID string) WorkflowIdentifier { return WorkflowIdentifier{ diff --git a/common/definition/workflowidentifier_test.go b/common/definition/workflowidentifier_test.go new file mode 100644 index 00000000000..bcd16de5de1 --- /dev/null +++ b/common/definition/workflowidentifier_test.go @@ -0,0 +1,71 @@ +// The MIT License (MIT) + +// Copyright (c) 2017-2020 Uber Technologies Inc. + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package definition + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +// WorkflowIdentifierTestSuite is a test suite for the WorkflowIdentifier struct. +type WorkflowIdentifierTestSuite struct { + suite.Suite +} + +// SetupTest runs before each test function in the suite. +func (suite *WorkflowIdentifierTestSuite) SetupTest() { + // Any necessary setup would go here, but it's not required in this case. +} + +// TestSize verifies the Size method of WorkflowIdentifier. +func (suite *WorkflowIdentifierTestSuite) TestSize() { + tests := []struct { + wi WorkflowIdentifier + expected uint64 + }{ + { + wi: NewWorkflowIdentifier("domain", "workflow", "run"), + expected: uint64(len("domain") + len("workflow") + len("run") + 3*16), + }, + { + wi: NewWorkflowIdentifier("", "", ""), + expected: uint64(3 * 16), + }, + { + wi: NewWorkflowIdentifier("a", "b", "c"), + expected: uint64(3 + 3*16), + }, + } + + for _, test := range tests { + size := test.wi.Size() + assert.Equal(suite.T(), test.expected, size) + } +} + +// TestWorkflowIdentifierTestSuite runs the test suite. +func TestWorkflowIdentifierTestSuite(t *testing.T) { + suite.Run(t, new(WorkflowIdentifierTestSuite)) +} diff --git a/service/history/execution/context.go b/service/history/execution/context.go index e7b87f8a64b..2e51512f732 100644 --- a/service/history/execution/context.go +++ b/service/history/execution/context.go @@ -162,6 +162,8 @@ type ( ctx context.Context, now time.Time, ) error + + Size() uint64 } ) @@ -1385,6 +1387,27 @@ func (c *contextImpl) ReapplyEvents( ) } +func (c *contextImpl) Size() uint64 { + + var size int + // Estimate size of strings + size += len(c.domainID) + + size += len(c.workflowExecution.GetWorkflowID()) + len(c.workflowExecution.GetRunID()) + size += 3 * common.StringSizeOverheadBytes + size += int(c.shard.Size()) + + size += 3 * 8 // logger + size += 512 // MetricsClient estimation + size += 256 // ExecutionManager estimation + size += 8 // Mutex + size += c.mutableState.GetEstimatedMutableStateSize() + size += 8 // stats pointer + + size += 18 * 8 // 18 function pointers with 8 bytes each + return uint64(size) +} + func isOperationPossiblySuccessfulError(err error) bool { switch err.(type) { case nil: diff --git a/service/history/execution/context_mock.go b/service/history/execution/context_mock.go index b8b765e0ba7..7c56a3959b2 100644 --- a/service/history/execution/context_mock.go +++ b/service/history/execution/context_mock.go @@ -304,6 +304,20 @@ func (mr *MockContextMockRecorder) SetWorkflowExecution(mutableState any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWorkflowExecution", reflect.TypeOf((*MockContext)(nil).SetWorkflowExecution), mutableState) } +// Size mocks base method. +func (m *MockContext) Size() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Size") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// Size indicates an expected call of Size. +func (mr *MockContextMockRecorder) Size() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockContext)(nil).Size)) +} + // Unlock mocks base method. func (m *MockContext) Unlock() { m.ctrl.T.Helper() diff --git a/service/history/execution/mutable_state.go b/service/history/execution/mutable_state.go index 64ddbe574b1..ea12d2d98cc 100644 --- a/service/history/execution/mutable_state.go +++ b/service/history/execution/mutable_state.go @@ -236,5 +236,7 @@ type ( GetHistorySize() int64 SetHistorySize(size int64) + + GetEstimatedMutableStateSize() int } ) diff --git a/service/history/execution/mutable_state_builder.go b/service/history/execution/mutable_state_builder.go index f33044bc310..e904f95f331 100644 --- a/service/history/execution/mutable_state_builder.go +++ b/service/history/execution/mutable_state_builder.go @@ -1595,6 +1595,11 @@ func (e *mutableStateBuilder) SetHistorySize(size int64) { e.executionStats.HistorySize = size } +func (e *mutableStateBuilder) GetEstimatedMutableStateSize() int { + // TODO: To be implemented + return 0 +} + func (e *mutableStateBuilder) prepareCloseTransaction( transactionPolicy TransactionPolicy, ) error { diff --git a/service/history/execution/mutable_state_mock.go b/service/history/execution/mutable_state_mock.go index 453a600ae69..1cb1fa610c7 100644 --- a/service/history/execution/mutable_state_mock.go +++ b/service/history/execution/mutable_state_mock.go @@ -1207,6 +1207,20 @@ func (mr *MockMutableStateMockRecorder) GetDomainEntry() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDomainEntry", reflect.TypeOf((*MockMutableState)(nil).GetDomainEntry)) } +// GetEstimatedMutableStateSize mocks base method. +func (m *MockMutableState) GetEstimatedMutableStateSize() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEstimatedMutableStateSize") + ret0, _ := ret[0].(int) + return ret0 +} + +// GetEstimatedMutableStateSize indicates an expected call of GetEstimatedMutableStateSize. +func (mr *MockMutableStateMockRecorder) GetEstimatedMutableStateSize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEstimatedMutableStateSize", reflect.TypeOf((*MockMutableState)(nil).GetEstimatedMutableStateSize)) +} + // GetExecutionInfo mocks base method. func (m *MockMutableState) GetExecutionInfo() *persistence.WorkflowExecutionInfo { m.ctrl.T.Helper() diff --git a/service/history/shard/context.go b/service/history/shard/context.go index ad1cc95854b..1041144e53a 100644 --- a/service/history/shard/context.go +++ b/service/history/shard/context.go @@ -115,6 +115,8 @@ type ( ReplicateFailoverMarkers(ctx context.Context, markers []*persistence.FailoverMarkerTask) error AddingPendingFailoverMarker(*types.FailoverMarkerAttributes) error ValidateAndUpdateFailoverMarkers() ([]*types.FailoverMarkerAttributes, error) + + Size() uint64 } // TransferFailoverLevel contains corresponding start / end level @@ -1413,6 +1415,11 @@ func (s *contextImpl) ValidateAndUpdateFailoverMarkers() ([]*types.FailoverMarke return s.shardInfo.PendingFailoverMarkers, nil } +func (s *contextImpl) Size() uint64 { + // TODO: To be implemented + return 0 +} + func acquireShard( shardItem *historyShardsItem, closeCallback func(int, *historyShardsItem), diff --git a/service/history/shard/context_mock.go b/service/history/shard/context_mock.go index 1da5a87dd57..59fc614aa86 100644 --- a/service/history/shard/context_mock.go +++ b/service/history/shard/context_mock.go @@ -637,6 +637,20 @@ func (mr *MockContextMockRecorder) SetEngine(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEngine", reflect.TypeOf((*MockContext)(nil).SetEngine), arg0) } +// Size mocks base method. +func (m *MockContext) Size() uint64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Size") + ret0, _ := ret[0].(uint64) + return ret0 +} + +// Size indicates an expected call of Size. +func (mr *MockContextMockRecorder) Size() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockContext)(nil).Size)) +} + // UpdateClusterReplicationLevel mocks base method. func (m *MockContext) UpdateClusterReplicationLevel(cluster string, lastTaskID int64) error { m.ctrl.T.Helper()