diff --git a/internal/persistence/leveldb/leveldb_persistence.go b/internal/persistence/leveldb/leveldb_persistence.go index b6911325..737dd011 100644 --- a/internal/persistence/leveldb/leveldb_persistence.go +++ b/internal/persistence/leveldb/leveldb_persistence.go @@ -721,7 +721,7 @@ func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string for _, entry := range currentSubStatus.Actions { if entry.Action == action { alreadyRecordedAction = true - entry.Count++ + entry.OccurrenceCount++ entry.LastOccurrence = fftypes.Now() if errInfo != nil { @@ -739,10 +739,10 @@ func (p *leveldbPersistence) AddSubStatusAction(ctx context.Context, txID string if !alreadyRecordedAction { // If this is an entirely new action for this status entry, add it to the list newAction := &apitypes.TxHistoryActionEntry{ - Time: fftypes.Now(), - Action: action, - LastOccurrence: fftypes.Now(), - Count: 1, + Time: fftypes.Now(), + Action: action, + LastOccurrence: fftypes.Now(), + OccurrenceCount: 1, } if errInfo != nil { diff --git a/internal/persistence/leveldb/leveldb_persistence_test.go b/internal/persistence/leveldb/leveldb_persistence_test.go index f90fac99..c4a688ee 100644 --- a/internal/persistence/leveldb/leveldb_persistence_test.go +++ b/internal/persistence/leveldb/leveldb_persistence_test.go @@ -933,7 +933,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 2, len(txh.History[0].Actions)) assert.Equal(t, txh.History[0].Actions[1].Action, apitypes.TxActionRetrieveGasPrice) - assert.Equal(t, 2, txh.History[0].Actions[1].Count) + assert.Equal(t, 2, txh.History[0].Actions[1].OccurrenceCount) // Add the same action but with new error information should update the last error field err = p.AddSubStatusAction(ctx, mtx.ID, apitypes.TxSubStatusReceived, apitypes.TxActionRetrieveGasPrice, nil, fftypes.JSONAnyPtr(`{"gasError":"Acme Gas Oracle RC=67890"}`)) @@ -952,7 +952,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 3, len(txh.History[0].Actions)) assert.Equal(t, txh.History[0].Actions[2].Action, apitypes.TxActionSubmitTransaction) - assert.Equal(t, 1, txh.History[0].Actions[2].Count) + assert.Equal(t, 1, txh.History[0].Actions[2].OccurrenceCount) assert.Nil(t, txh.History[0].Actions[2].LastErrorTime) // Add one more type of action @@ -964,7 +964,7 @@ func TestManagedTXSubStatusAction(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 4, len(txh.History[0].Actions)) assert.Equal(t, txh.History[0].Actions[3].Action, apitypes.TxActionReceiveReceipt) - assert.Equal(t, 1, txh.History[0].Actions[3].Count) + assert.Equal(t, 1, txh.History[0].Actions[3].OccurrenceCount) assert.Nil(t, txh.History[0].Actions[3].LastErrorTime) // History is the complete list of unique sub-status types and actions diff --git a/internal/persistence/persistence.go b/internal/persistence/persistence.go index a46678e9..bc81fac9 100644 --- a/internal/persistence/persistence.go +++ b/internal/persistence/persistence.go @@ -130,10 +130,10 @@ var TXHistoryFilters = &ffapi.QueryFields{ "lastoccurrence": &ffapi.TimeField{}, "substatus": &ffapi.StringField{}, "action": &ffapi.StringField{}, - // "count": &ffapi.Int64Field{}, /* must not add this as count is a reserved word on the API */ - "lasterror": &ffapi.JSONField{}, - "lasterrortime": &ffapi.TimeField{}, - "lastinfo": &ffapi.JSONField{}, + "occurrences": &ffapi.Int64Field{}, + "lasterror": &ffapi.JSONField{}, + "lasterrortime": &ffapi.TimeField{}, + "lastinfo": &ffapi.JSONField{}, } type NextNonceCallback func(ctx context.Context, signer string) (uint64, error) diff --git a/internal/persistence/postgres/transactions_test.go b/internal/persistence/postgres/transactions_test.go index c5c16ab9..07a63c1e 100644 --- a/internal/persistence/postgres/transactions_test.go +++ b/internal/persistence/postgres/transactions_test.go @@ -168,13 +168,13 @@ func TestTransactionBasicValidationPSQL(t *testing.T) { Time: mtx.History[0].Time, Actions: []*apitypes.TxHistoryActionEntry{ // newest first { - Action: apitypes.TxActionSubmitTransaction, - Time: mtx.History[0].Actions[0].Time, - LastOccurrence: mtx.History[0].Actions[0].LastOccurrence, - Count: 1, - LastInfo: fftypes.JSONAnyPtr(`{"txhash":"0x12345"}`), - LastError: nil, - LastErrorTime: nil, + Action: apitypes.TxActionSubmitTransaction, + Time: mtx.History[0].Actions[0].Time, + LastOccurrence: mtx.History[0].Actions[0].LastOccurrence, + OccurrenceCount: 1, + LastInfo: fftypes.JSONAnyPtr(`{"txhash":"0x12345"}`), + LastError: nil, + LastErrorTime: nil, }, }, }, @@ -183,22 +183,22 @@ func TestTransactionBasicValidationPSQL(t *testing.T) { Time: mtx.History[1].Time, Actions: []*apitypes.TxHistoryActionEntry{ // newest first { - Action: apitypes.TxActionSubmitTransaction, - Time: mtx.History[1].Actions[0].Time, - LastOccurrence: mtx.History[1].Actions[0].LastOccurrence, - Count: 2, - LastInfo: nil, - LastError: fftypes.JSONAnyPtr(`"failed to submit 2"`), - LastErrorTime: mtx.History[1].Actions[0].LastErrorTime, + Action: apitypes.TxActionSubmitTransaction, + Time: mtx.History[1].Actions[0].Time, + LastOccurrence: mtx.History[1].Actions[0].LastOccurrence, + OccurrenceCount: 2, + LastInfo: nil, + LastError: fftypes.JSONAnyPtr(`"failed to submit 2"`), + LastErrorTime: mtx.History[1].Actions[0].LastErrorTime, }, { - Action: apitypes.TxActionAssignNonce, - Time: mtx.History[1].Actions[1].Time, - LastOccurrence: mtx.History[1].Actions[1].LastOccurrence, - Count: 1, - LastInfo: fftypes.JSONAnyPtr(`{"nonce":"11111"}`), - LastError: nil, - LastErrorTime: nil, + Action: apitypes.TxActionAssignNonce, + Time: mtx.History[1].Actions[1].Time, + LastOccurrence: mtx.History[1].Actions[1].LastOccurrence, + OccurrenceCount: 1, + LastInfo: fftypes.JSONAnyPtr(`{"nonce":"11111"}`), + LastError: nil, + LastErrorTime: nil, }, }, }, diff --git a/internal/persistence/postgres/txhistory.go b/internal/persistence/postgres/txhistory.go index 3c3f628c..e2163d57 100644 --- a/internal/persistence/postgres/txhistory.go +++ b/internal/persistence/postgres/txhistory.go @@ -51,6 +51,7 @@ func (p *sqlPersistence) newTXHistoryCollection() *dbsql.CrudBase[*apitypes.TXHi "lasterror": "error", "lasterrortime": "error_time", "lastinfo": "info", + "occurrences": "count", }, PatchDisabled: true, TimesDisabled: true, @@ -67,7 +68,7 @@ func (p *sqlPersistence) newTXHistoryCollection() *dbsql.CrudBase[*apitypes.TXHi case "action": return &inst.Action case "count": - return &inst.Count + return &inst.OccurrenceCount case "time": return &inst.Time case "last_occurrence": @@ -99,12 +100,12 @@ func (p *sqlPersistence) AddSubStatusAction(ctx context.Context, txID string, su TransactionID: txID, SubStatus: subStatus, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Count: 1, - Time: now, - LastOccurrence: now, - Action: action, - LastInfo: persistence.JSONOrString(info), // guard against bad JSON - LastError: persistence.JSONOrString(errInfo), // guard against bad JSON + OccurrenceCount: 1, + Time: now, + LastOccurrence: now, + Action: action, + LastInfo: persistence.JSONOrString(info), // guard against bad JSON + LastError: persistence.JSONOrString(errInfo), // guard against bad JSON }, } if errInfo != nil { @@ -117,8 +118,8 @@ func (p *sqlPersistence) AddSubStatusAction(ctx context.Context, txID string, su func (p *sqlPersistence) compressHistory(ctx context.Context, txID string) error { result, err := p.buildHistorySummary(ctx, txID, false, 0, func(from, to *apitypes.TXHistoryRecord) error { update := persistence.TXHistoryFilters.NewUpdate(ctx). - Set("count", to.Count+1). // increment the count - Set("time", from.Time) // move the time on the newer record to be the time of the older record merged in + Set("occurrences", to.OccurrenceCount+1). // increment the count + Set("time", from.Time) // move the time on the newer record to be the time of the older record merged in if err := p.txHistory.Update(ctx, to.ID.String(), update); err != nil { return err } @@ -170,13 +171,13 @@ func (p *sqlPersistence) buildHistorySummary(ctx context.Context, txID string, b // Actions are also in descending order if buildResult { actionEntry := &apitypes.TxHistoryActionEntry{ - Time: h.Time, - LastOccurrence: h.LastOccurrence, - Action: h.Action, - Count: h.Count, - LastInfo: h.LastInfo, - LastError: h.LastError, - LastErrorTime: h.LastErrorTime, + Time: h.Time, + LastOccurrence: h.LastOccurrence, + Action: h.Action, + OccurrenceCount: h.OccurrenceCount, + LastInfo: h.LastInfo, + LastError: h.LastError, + LastErrorTime: h.LastErrorTime, } statusEntry := r.entries[len(r.entries)-1] statusEntry.Actions = append(statusEntry.Actions, actionEntry) @@ -193,11 +194,11 @@ func (p *sqlPersistence) buildHistorySummary(ctx context.Context, txID string, b return nil, err } } - lastRecordSameSubStatus.Count++ + lastRecordSameSubStatus.OccurrenceCount++ if buildResult { statusEntry := r.entries[len(r.entries)-1] actionEntry := statusEntry.Actions[len(statusEntry.Actions)-1] - actionEntry.Count++ + actionEntry.OccurrenceCount++ actionEntry.Time = h.Time } } diff --git a/internal/persistence/postgres/txhistory_test.go b/internal/persistence/postgres/txhistory_test.go index 162f0a0e..125ef59d 100644 --- a/internal/persistence/postgres/txhistory_test.go +++ b/internal/persistence/postgres/txhistory_test.go @@ -114,8 +114,8 @@ func TestTXHistoryCompressionPSQL(t *testing.T) { if h.LastError != nil { assert.NotNil(t, h.LastErrorTime) } - assert.NotZero(t, h.Count) - if h.Count == 1 { + assert.NotZero(t, h.OccurrenceCount) + if h.OccurrenceCount == 1 { assert.Equal(t, h.LastOccurrence.String(), h.Time.String()) } else { assert.GreaterOrEqual(t, *h.LastOccurrence.Time(), *h.Time.Time()) @@ -130,54 +130,54 @@ func TestTXHistoryCompressionPSQL(t *testing.T) { TransactionID: txID, SubStatus: apitypes.TxSubStatusConfirmed, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Action: apitypes.TxActionConfirmTransaction, - Count: 1, - LastInfo: fftypes.JSONAnyPtr(`{"confirmed":true}`), + Action: apitypes.TxActionConfirmTransaction, + OccurrenceCount: 1, + LastInfo: fftypes.JSONAnyPtr(`{"confirmed":true}`), }, }, { TransactionID: txID, SubStatus: apitypes.TxSubStatusTracking, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Action: apitypes.TxActionTimeout, - Count: 2, - LastError: fftypes.JSONAnyPtr(`"some error 555"`), + Action: apitypes.TxActionTimeout, + OccurrenceCount: 2, + LastError: fftypes.JSONAnyPtr(`"some error 555"`), }, }, { TransactionID: txID, SubStatus: apitypes.TxSubStatusTracking, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Action: apitypes.TxActionReceiveReceipt, - Count: 3, - LastInfo: fftypes.JSONAnyPtr(`{"transactionHash":"0x333333","blockNumber":"33333"}`), + Action: apitypes.TxActionReceiveReceipt, + OccurrenceCount: 3, + LastInfo: fftypes.JSONAnyPtr(`{"transactionHash":"0x333333","blockNumber":"33333"}`), }, }, { TransactionID: txID, SubStatus: apitypes.TxSubStatusTracking, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Action: apitypes.TxActionSubmitTransaction, - Count: 1, - LastInfo: fftypes.JSONAnyPtr(`{"transactionHash":"0x12345"}`), + Action: apitypes.TxActionSubmitTransaction, + OccurrenceCount: 1, + LastInfo: fftypes.JSONAnyPtr(`{"transactionHash":"0x12345"}`), }, }, { TransactionID: txID, SubStatus: apitypes.TxSubStatusReceived, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Action: apitypes.TxActionSubmitTransaction, - Count: 3, - LastError: fftypes.JSONAnyPtr(`"failed to submit 333"`), + Action: apitypes.TxActionSubmitTransaction, + OccurrenceCount: 3, + LastError: fftypes.JSONAnyPtr(`"failed to submit 333"`), }, }, { TransactionID: txID, SubStatus: apitypes.TxSubStatusReceived, TxHistoryActionEntry: apitypes.TxHistoryActionEntry{ - Action: apitypes.TxActionAssignNonce, - Count: 1, - LastInfo: fftypes.JSONAnyPtr(`{"nonce":"11111"}`), + Action: apitypes.TxActionAssignNonce, + OccurrenceCount: 1, + LastInfo: fftypes.JSONAnyPtr(`{"nonce":"11111"}`), }, }, }, history) diff --git a/pkg/apitypes/managed_tx.go b/pkg/apitypes/managed_tx.go index 7479083e..1a7ee1de 100644 --- a/pkg/apitypes/managed_tx.go +++ b/pkg/apitypes/managed_tx.go @@ -99,13 +99,13 @@ const ( // when they occur multiple times. So if we are retrying the same set of actions over and over // again the list of actions does not grow. type TxHistoryActionEntry struct { - Time *fftypes.FFTime `json:"time"` - Action TxAction `json:"action"` - LastOccurrence *fftypes.FFTime `json:"lastOccurrence,omitempty"` - Count int `json:"count"` - LastError *fftypes.JSONAny `json:"lastError,omitempty"` - LastErrorTime *fftypes.FFTime `json:"lastErrorTime,omitempty"` - LastInfo *fftypes.JSONAny `json:"lastInfo,omitempty"` + Time *fftypes.FFTime `json:"time"` + Action TxAction `json:"action"` + LastOccurrence *fftypes.FFTime `json:"lastOccurrence,omitempty"` + OccurrenceCount int `json:"count"` // serialized as count for historical reasons + LastError *fftypes.JSONAny `json:"lastError,omitempty"` + LastErrorTime *fftypes.FFTime `json:"lastErrorTime,omitempty"` + LastInfo *fftypes.JSONAny `json:"lastInfo,omitempty"` } // TXHistoryRecord are the sequential persisted records, which might be state transitions, or actions within the current state.