Repository: stellar/go
Pull request #5362 (2024-06-27 21:50:44)

support/storage: Add GetLatestLedgerSequence method to Archive interface
<!-- If you're making a doc PR or something tiny where the below is irrelevant, delete this
template and use a short description, but in your description aim to include both what the
change is, and why it is being made, with enough context for anyone to understand. -->

<details>
  <summary>PR Checklist</summary>
  
### PR Structure

* [ ] This PR has reasonably narrow scope (if not, break it down into smaller PRs).
* [ ] This PR avoids mixing refactoring changes with feature changes (split into two PRs
  otherwise).
* [ ] This PR's title starts with name of package that is most changed in the PR, ex.
  `services/friendbot`, or `all` or `doc` if the changes are broad or impact many
  packages.

### Thoroughness

* [ ] This PR adds tests for the most critical parts of the new functionality or fixes.
* [ ] I've updated any docs ([developer docs](https://developers.stellar.org/api/), `.md`
  files, etc... affected by this change). Take a look in the `docs` folder for a given service,
  like [this one](https://github.com/stellar/go/tree/master/services/horizon/internal/docs).

### Release planning

* [ ] I've updated the relevant CHANGELOG ([here](services/horizon/CHANGELOG.md) for Horizon) if
  needed with deprecations, added features, breaking changes, and DB schema changes.
* [ ] I've decided if this PR requires a new major/minor version according to
  [semver](https://semver.org/), or if it's mainly a patch change. The PR is targeted at the next
  release branch if it's not a patch change.
</details>

### What

- Add `GetLatestLedgerSequence` method to Archive interface.
- Removed unused support/datastore/history_archive.go

### Why
Code cleanup

### Known limitations

N/A



Patch:
diff --git a/exp/services/ledgerexporter/internal/config.go b/exp/services/ledgerexporter/internal/config.go
index d5aad532..013a3ef8 100644
--- a/exp/services/ledgerexporter/internal/config.go
+++ b/exp/services/ledgerexporter/internal/config.go
@@ -124,8 +124,8 @@ func (config *Config) ValidateAndSetLedgerRange(ctx context.Context, archive his
 		return errors.New("invalid end value, must be greater than start")
 	}
 
-	latestNetworkLedger, err := datastore.GetLatestLedgerSequenceFromHistoryArchives(archive)
-	latestNetworkLedger = latestNetworkLedger + (datastore.GetHistoryArchivesCheckPointFrequency() * 2)
+	latestNetworkLedger, err := archive.GetLatestLedgerSequence()
+	latestNetworkLedger = latestNetworkLedger + (archive.GetCheckpointManager().GetCheckpointFrequency() * 2)
 
 	if err != nil {
 		return errors.Wrap(err, "Failed to retrieve the latest ledger sequence from history archives.")
@@ -189,7 +189,7 @@ func (config *Config) GenerateCaptiveCoreConfig(coreBinFromPath string) (ledgerb
 		BinaryPath:          config.StellarCoreConfig.StellarCoreBinaryPath,
 		NetworkPassphrase:   params.NetworkPassphrase,
 		HistoryArchiveURLs:  params.HistoryArchiveURLs,
-		CheckpointFrequency: datastore.GetHistoryArchivesCheckPointFrequency(),
+		CheckpointFrequency: historyarchive.DefaultCheckpointFrequency,
 		Log:                 logger.WithField("subservice", "stellar-core"),
 		Toml:                captiveCoreToml,
 		UserAgent:           "ledger-exporter",
diff --git a/historyarchive/archive.go b/historyarchive/archive.go
index 4f9e1438..d97471b4 100644
--- a/historyarchive/archive.go
+++ b/historyarchive/archive.go
@@ -71,6 +71,7 @@ type ArchiveInterface interface {
 	GetLedgerHeader(chk uint32) (xdr.LedgerHeaderHistoryEntry, error)
 	GetRootHAS() (HistoryArchiveState, error)
 	GetLedgers(start, end uint32) (map[uint32]*Ledger, error)
+	GetLatestLedgerSequence() (uint32, error)
 	GetCheckpointHAS(chk uint32) (HistoryArchiveState, error)
 	PutCheckpointHAS(chk uint32, has HistoryArchiveState, opts *CommandOptions) error
 	PutRootHAS(has HistoryArchiveState, opts *CommandOptions) error
@@ -176,6 +177,16 @@ func (a *Archive) PutPathHAS(path string, has HistoryArchiveState, opts *Command
 	return a.backend.PutFile(path, io.NopCloser(bytes.NewReader(buf)))
 }
 
+func (a *Archive) GetLatestLedgerSequence() (uint32, error) {
+	has, err := a.GetRootHAS()
+	if err != nil {
+		log.Error("Error getting root HAS from archive", err)
+		return 0, errors.Wrap(err, "failed to retrieve the latest ledger sequence from history archive")
+	}
+
+	return has.CurrentLedger, nil
+}
+
 func (a *Archive) BucketExists(bucket Hash) (bool, error) {
 	return a.cachedExists(BucketPath(bucket))
 }
diff --git a/historyarchive/archive_pool.go b/historyarchive/archive_pool.go
index 48178ade..28967d8a 100644
--- a/historyarchive/archive_pool.go
+++ b/historyarchive/archive_pool.go
@@ -204,6 +204,16 @@ func (pa *ArchivePool) GetCheckpointHAS(chk uint32) (HistoryArchiveState, error)
 	})
 }
 
+func (pa *ArchivePool) GetLatestLedgerSequence() (uint32, error) {
+	has, err := pa.GetRootHAS()
+	if err != nil {
+		log.Error("Error getting root HAS from archive", err)
+		return 0, errors.Wrap(err, "failed to retrieve the latest ledger sequence from history archive")
+	}
+
+	return has.CurrentLedger, nil
+}
+
 func (pa *ArchivePool) PutCheckpointHAS(chk uint32, has HistoryArchiveState, opts *CommandOptions) error {
 	return pa.runRoundRobin(func(ai ArchiveInterface) error {
 		return ai.PutCheckpointHAS(chk, has, opts)
diff --git a/historyarchive/mocks.go b/historyarchive/mocks.go
index fa5716e5..efe333cd 100644
--- a/historyarchive/mocks.go
+++ b/historyarchive/mocks.go
@@ -10,6 +10,11 @@ type MockArchive struct {
 	mock.Mock
 }
 
+func (m *MockArchive) GetLatestLedgerSequence() (uint32, error) {
+	a := m.Called()
+	return a.Get(0).(uint32), a.Error(1)
+}
+
 func (m *MockArchive) GetCheckpointManager() CheckpointManager {
 	a := m.Called()
 	return a.Get(0).(CheckpointManager)
diff --git a/support/datastore/history_archive.go b/support/datastore/history_archive.go
deleted file mode 100644
index 9fd291ba..00000000
--- a/support/datastore/history_archive.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package datastore
-
-import (
-	"context"
-
-	log "github.com/sirupsen/logrus"
-
-	"github.com/stellar/go/historyarchive"
-	"github.com/stellar/go/network"
-	"github.com/stellar/go/support/errors"
-	supportlog "github.com/stellar/go/support/log"
-	"github.com/stellar/go/support/storage"
-)
-
-const (
-	Pubnet  = "pubnet"
-	Testnet = "testnet"
-)
-
-func CreateHistoryArchiveFromNetworkName(ctx context.Context, networkName string, userAgent string, logger *supportlog.Entry) (historyarchive.ArchiveInterface, error) {
-	var historyArchiveUrls []string
-	switch networkName {
-	case Pubnet:
-		historyArchiveUrls = network.PublicNetworkhistoryArchiveURLs
-	case Testnet:
-		historyArchiveUrls = network.TestNetworkhistoryArchiveURLs
-	default:
-		return nil, errors.Errorf("Invalid network name %s", networkName)
-	}
-
-	return historyarchive.NewArchivePool(historyArchiveUrls, historyarchive.ArchiveOptions{
-		Logger: logger,
-		ConnectOptions: storage.ConnectOptions{
-			UserAgent: userAgent,
-			Context:   ctx,
-		},
-	})
-}
-
-func GetLatestLedgerSequenceFromHistoryArchives(archive historyarchive.ArchiveInterface) (uint32, error) {
-	has, err := archive.GetRootHAS()
-	if err != nil {
-		log.Error("Error getting root HAS from archives", err)
-		return 0, errors.Wrap(err, "failed to retrieve the latest ledger sequence from any history archive")
-	}
-
-	return has.CurrentLedger, nil
-}
-
-func GetHistoryArchivesCheckPointFrequency() uint32 {
-	// this could evolve to use other sources for checkpoint freq
-	return historyarchive.DefaultCheckpointFrequency
-}
diff --git a/support/datastore/resumeablemanager.go b/support/datastore/resumeablemanager.go
index 35031d73..7e6b03df 100644
--- a/support/datastore/resumeablemanager.go
+++ b/support/datastore/resumeablemanager.go
@@ -62,12 +62,12 @@ func (rm resumableManagerService) FindStart(ctx context.Context, start, end uint
 	networkLatest := uint32(0)
 	if end < 1 {
 		var latestErr error
-		networkLatest, latestErr = GetLatestLedgerSequenceFromHistoryArchives(rm.archive)
+		networkLatest, latestErr = rm.archive.GetLatestLedgerSequence()
 		if latestErr != nil {
 			err := errors.Wrap(latestErr, "Resumability of requested export ledger range, was not able to get latest ledger from network")
 			return 0, false, err
 		}
-		networkLatest = networkLatest + (GetHistoryArchivesCheckPointFrequency() * 2)
+		networkLatest = networkLatest + (rm.archive.GetCheckpointManager().GetCheckpointFrequency() * 2)
 		log.Infof("Resumability computed effective latest network ledger including padding of checkpoint frequency to be %d", networkLatest)
 
 		if start > networkLatest {


Test patch:
diff --git a/exp/services/ledgerexporter/internal/config_test.go b/exp/services/ledgerexporter/internal/config_test.go
index f782de5e..d1c24cb1 100644
--- a/exp/services/ledgerexporter/internal/config_test.go
+++ b/exp/services/ledgerexporter/internal/config_test.go
@@ -5,19 +5,20 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/stellar/go/historyarchive"
 	"github.com/stellar/go/network"
-	"github.com/stellar/go/support/datastore"
 
 	"github.com/stretchr/testify/require"
-
-	"github.com/stellar/go/historyarchive"
 )
 
 func TestNewConfig(t *testing.T) {
 	ctx := context.Background()
 
 	mockArchive := &historyarchive.MockArchive{}
-	mockArchive.On("GetRootHAS").Return(historyarchive.HistoryArchiveState{CurrentLedger: 5}, nil).Once()
+	mockArchive.On("GetLatestLedgerSequence").Return(uint32(5), nil).Once()
+	mockArchive.On("GetCheckpointManager").
+		Return(historyarchive.NewCheckpointManager(
+			historyarchive.DefaultCheckpointFrequency)).Once()
 
 	config, err := NewConfig(
 		RuntimeSettings{StartLedger: 2, EndLedger: 3, ConfigFilePath: "test/test.toml", Mode: Append}, nil)
@@ -198,7 +199,7 @@ func TestInvalidCaptiveCoreTomlPath(t *testing.T) {
 
 func TestValidateStartAndEndLedger(t *testing.T) {
 	latestNetworkLedger := uint32(20000)
-	latestNetworkLedgerPadding := datastore.GetHistoryArchivesCheckPointFrequency() * 2
+	latestNetworkLedgerPadding := historyarchive.DefaultCheckpointFrequency * 2
 
 	tests := []struct {
 		name        string
@@ -282,7 +283,10 @@ func TestValidateStartAndEndLedger(t *testing.T) {
 
 	ctx := context.Background()
 	mockArchive := &historyarchive.MockArchive{}
-	mockArchive.On("GetRootHAS").Return(historyarchive.HistoryArchiveState{CurrentLedger: latestNetworkLedger}, nil)
+	mockArchive.On("GetLatestLedgerSequence").Return(latestNetworkLedger, nil)
+	mockArchive.On("GetCheckpointManager").
+		Return(historyarchive.NewCheckpointManager(
+			historyarchive.DefaultCheckpointFrequency))
 
 	mockedHasCtr := 0
 	for _, tt := range tests {
@@ -302,7 +306,7 @@ func TestValidateStartAndEndLedger(t *testing.T) {
 			}
 		})
 	}
-	mockArchive.AssertNumberOfCalls(t, "GetRootHAS", mockedHasCtr)
+	mockArchive.AssertExpectations(t)
 }
 
 func TestAdjustedLedgerRangeBoundedMode(t *testing.T) {
@@ -358,7 +362,10 @@ func TestAdjustedLedgerRangeBoundedMode(t *testing.T) {
 
 	ctx := context.Background()
 	mockArchive := &historyarchive.MockArchive{}
-	mockArchive.On("GetRootHAS").Return(historyarchive.HistoryArchiveState{CurrentLedger: 500}, nil).Times(len(tests))
+	mockArchive.On("GetLatestLedgerSequence").Return(uint32(500), nil)
+	mockArchive.On("GetCheckpointManager").
+		Return(historyarchive.NewCheckpointManager(
+			historyarchive.DefaultCheckpointFrequency))
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -421,7 +428,10 @@ func TestAdjustedLedgerRangeUnBoundedMode(t *testing.T) {
 	ctx := context.Background()
 
 	mockArchive := &historyarchive.MockArchive{}
-	mockArchive.On("GetRootHAS").Return(historyarchive.HistoryArchiveState{CurrentLedger: 500}, nil).Times(len(tests))
+	mockArchive.On("GetLatestLedgerSequence").Return(uint32(500), nil)
+	mockArchive.On("GetCheckpointManager").
+		Return(historyarchive.NewCheckpointManager(
+			historyarchive.DefaultCheckpointFrequency))
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/support/datastore/resumablemanager_test.go b/support/datastore/resumablemanager_test.go
index 4616f9e4..4fc8738b 100644
--- a/support/datastore/resumablemanager_test.go
+++ b/support/datastore/resumablemanager_test.go
@@ -282,8 +282,12 @@ func TestResumability(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			mockArchive := &historyarchive.MockArchive{}
-			mockArchive.On("GetRootHAS").Return(historyarchive.HistoryArchiveState{CurrentLedger: tt.latestLedger}, tt.archiveError).Once()
-
+			mockArchive.On("GetLatestLedgerSequence").Return(tt.latestLedger, tt.archiveError).Once()
+			if tt.archiveError == nil {
+				mockArchive.On("GetCheckpointManager").
+					Return(historyarchive.NewCheckpointManager(
+						historyarchive.DefaultCheckpointFrequency)).Once()
+			}
 			mockDataStore := &MockDataStore{}
 			tt.registerMockCalls(mockDataStore)