@@ -22,9 +22,11 @@ var ErrLoadTestDone = fmt.Errorf("the load test is done")
2222
2323// LedgerBackend is used to load test ingestion.
2424// LedgerBackend will take a file of synthetically generated ledgers (see
25- // services/horizon/internal/integration/generate_ledgers_test.go) and merge those ledgers
26- // with real ledgers from the Stellar network. The merged ledgers will then be replayed to
27- // the ingesting down stream system at a configurable rate.
25+ // services/horizon/internal/integration/generate_ledgers_test.go) and replay
26+ // them to the downstream ingesting system at a configurable rate.
27+ // It is also possible to merge the synthetically generated ledgers with real
28+ // ledgers from the network. To enable the merging behavior, configure the
29+ // LedgerBackend field in LedgerBackendConfig.
2830type LedgerBackend struct {
2931 config LedgerBackendConfig
3032 mergedLedgersFilePath string
@@ -44,13 +46,12 @@ type LedgerBackendConfig struct {
4446 // NetworkPassphrase is the passphrase of the Stellar network from where the real ledgers
4547 // will be obtained
4648 NetworkPassphrase string
47- // LedgerBackend is the source of the real ledgers
49+ // LedgerBackend is an optional parameter. When LedgerBackend is configured, ledgers from
50+ // LedgerBackend will be merged with the synthetic ledgers from LedgersFilePath.
4851 LedgerBackend ledgerbackend.LedgerBackend
49- // LedgersFilePath is a file containing the synthetic ledgers that will be combined with the
50- // real ledgers and then replayed by LedgerBackend
52+ // LedgersFilePath is a file containing the synthetic ledgers that will be replayed to
53+ // the downstream ingesting system.
5154 LedgersFilePath string
52- // LedgerEntriesFilePath is a file containing the ledger entry fixtures for the synthetic ledgers
53- LedgerEntriesFilePath string
5455 // LedgerCloseDuration is the rate at which ledgers will be replayed from LedgerBackend
5556 LedgerCloseDuration time.Duration
5657}
@@ -73,35 +74,6 @@ func (r *LedgerBackend) GetLatestLedgerSequence(ctx context.Context) (uint32, er
7374 return r .latestLedgerSeq , nil
7475}
7576
76- func readLedgerEntries (path string ) ([]xdr.LedgerEntry , error ) {
77- file , err := os .Open (path )
78- if err != nil {
79- return nil , fmt .Errorf ("could not open file: %w" , err )
80- }
81- stream , err := xdr .NewZstdStream (file )
82- if err != nil {
83- return nil , fmt .Errorf ("could not open zstd read stream: %w" , err )
84- }
85-
86- var entries []xdr.LedgerEntry
87- for {
88- var entry xdr.LedgerEntry
89- err = stream .ReadOne (& entry )
90- if err == io .EOF {
91- break
92- }
93- if err != nil {
94- return nil , fmt .Errorf ("could not read from zstd stream: %w" , err )
95- }
96- entries = append (entries , entry )
97- }
98-
99- if err = stream .Close (); err != nil {
100- return nil , fmt .Errorf ("could not close zstd stream: %w" , err )
101- }
102- return entries , nil
103- }
104-
10577func (r * LedgerBackend ) PrepareRange (ctx context.Context , ledgerRange ledgerbackend.Range ) error {
10678 r .lock .Lock ()
10779 defer r .lock .Unlock ()
@@ -115,10 +87,6 @@ func (r *LedgerBackend) PrepareRange(ctx context.Context, ledgerRange ledgerback
11587 }
11688 return fmt .Errorf ("PrepareRange() already called" )
11789 }
118- generatedLedgerEntries , err := readLedgerEntries (r .config .LedgerEntriesFilePath )
119- if err != nil {
120- return fmt .Errorf ("could not parse ledger entries file: %w" , err )
121- }
12290 generatedLedgersFile , err := os .Open (r .config .LedgersFilePath )
12391 if err != nil {
12492 return fmt .Errorf ("could not open ledgers file: %w" , err )
@@ -128,52 +96,6 @@ func (r *LedgerBackend) PrepareRange(ctx context.Context, ledgerRange ledgerback
12896 return fmt .Errorf ("could not open zstd stream for ledgers file: %w" , err )
12997 }
13098
131- err = r .config .LedgerBackend .PrepareRange (ctx , ledgerRange )
132- if err != nil {
133- return fmt .Errorf ("could not prepare range using real ledger backend: %w" , err )
134- }
135- cur := ledgerRange .From ()
136- firstLedger , err := r .config .LedgerBackend .GetLedger (ctx , cur )
137- if err != nil {
138- return fmt .Errorf ("could not get ledger %v from real ledger backend: %w" , cur , err )
139- }
140- var changes xdr.LedgerEntryChanges
141- // attach all ledger entry fixtures to the first ledger in the range
142- for i := 0 ; i < len (generatedLedgerEntries ); i ++ {
143- entry := generatedLedgerEntries [i ]
144- err = UpdateLedgerSeq (& entry , func (uint32 ) uint32 {
145- return cur
146- })
147- if err != nil {
148- return err
149- }
150- changes = append (changes , xdr.LedgerEntryChange {
151- Type : xdr .LedgerEntryChangeTypeLedgerEntryCreated ,
152- Created : & entry ,
153- })
154- }
155- var flag xdr.Uint32 = 1
156- switch firstLedger .V {
157- case 1 :
158- firstLedger .V1 .UpgradesProcessing = append (firstLedger .V1 .UpgradesProcessing , xdr.UpgradeEntryMeta {
159- Upgrade : xdr.LedgerUpgrade {
160- Type : xdr .LedgerUpgradeTypeLedgerUpgradeFlags ,
161- NewFlags : & flag ,
162- },
163- Changes : changes ,
164- })
165- case 2 :
166- firstLedger .V2 .UpgradesProcessing = append (firstLedger .V2 .UpgradesProcessing , xdr.UpgradeEntryMeta {
167- Upgrade : xdr.LedgerUpgrade {
168- Type : xdr .LedgerUpgradeTypeLedgerUpgradeFlags ,
169- NewFlags : & flag ,
170- },
171- Changes : changes ,
172- })
173- default :
174- return fmt .Errorf ("unsupported ledger version %d" , firstLedger .V )
175- }
176-
17799 mergedLedgersFile , err := os .CreateTemp ("" , "merged-ledgers" )
178100 if err != nil {
179101 return fmt .Errorf ("could not create merged ledgers file: %w" , err )
@@ -193,33 +115,27 @@ func (r *LedgerBackend) PrepareRange(ctx context.Context, ledgerRange ledgerback
193115 }
194116
195117 var latestLedgerSeq uint32
196- checkNetworkPassphrase := true
197- for cur = cur + 1 ; ! ledgerRange .Bounded () || cur <= ledgerRange .To (); cur ++ {
198- var ledger xdr.LedgerCloseMeta
199- ledger , err = r .config .LedgerBackend .GetLedger (ctx , cur )
200- if err != nil {
201- return fmt .Errorf ("could not get ledger %v from real ledger backend: %w" , cur , err )
202- }
118+ var firstLedger xdr.LedgerCloseMeta
119+ var validatedGeneratedLedgers , validatedNetworkLedgers bool
120+ for cur := ledgerRange .From (); ! ledgerRange .Bounded () || cur <= ledgerRange .To (); cur ++ {
203121 var generatedLedger xdr.LedgerCloseMeta
204122 if err = generatedLedgers .ReadOne (& generatedLedger ); err == io .EOF {
205123 break
206124 } else if err != nil {
207125 return fmt .Errorf ("could not get generated ledger: %w" , err )
208126 }
209- if checkNetworkPassphrase {
127+ if ! validatedGeneratedLedgers && generatedLedger . CountTransactions () > 0 {
210128 // Here we validate that the generated ledgers have the same network passphrase as the
211129 // ledgers sourced from the real network. This check only needs to be done once because
212130 // we assume all the generated ledgers have the same network passphrase.
213- if err = validateNetworkPassphrase (r .config .NetworkPassphrase , ledger ); err != nil {
214- return err
215- }
216131 if err = validateNetworkPassphrase (r .config .NetworkPassphrase , generatedLedger ); err != nil {
217132 return err
218133 }
219- checkNetworkPassphrase = false
134+ validatedGeneratedLedgers = true
220135 }
221- ledgerDiff := int64 (ledger .LedgerSequence ()) - int64 (generatedLedger .LedgerSequence ())
222- if err = MergeLedgers (& ledger , generatedLedger , func (cur uint32 ) uint32 {
136+
137+ ledgerDiff := int64 (cur ) - int64 (generatedLedger .LedgerSequence ())
138+ setLedgerSeq := func (cur uint32 ) uint32 {
223139 newLedgerSeq := int64 (cur ) + ledgerDiff
224140 if newLedgerSeq > math .MaxUint32 {
225141 panic (fmt .Sprintf (
@@ -236,11 +152,52 @@ func (r *LedgerBackend) PrepareRange(ctx context.Context, ledgerRange ledgerback
236152 return minLedger
237153 }
238154 return uint32 (newLedgerSeq )
239- }); err != nil {
240- return fmt .Errorf ("could not merge ledgers: %w" , err )
241155 }
242- if err = xdr .MarshalFramed (writer , ledger ); err != nil {
243- return fmt .Errorf ("could not marshal ledger to stream: %w" , err )
156+
157+ var ledger xdr.LedgerCloseMeta
158+ if r .config .LedgerBackend != nil {
159+ if cur == ledgerRange .From () {
160+ err = r .config .LedgerBackend .PrepareRange (ctx , ledgerRange )
161+ if err != nil {
162+ return fmt .Errorf ("could not prepare range using real ledger backend: %w" , err )
163+ }
164+ }
165+ ledger , err = r .config .LedgerBackend .GetLedger (ctx , cur )
166+ if err != nil {
167+ return fmt .Errorf ("could not get ledger %v from real ledger backend: %w" , cur , err )
168+ }
169+ if ! validatedNetworkLedgers && ledger .CountTransactions () > 0 {
170+ if err = validateNetworkPassphrase (r .config .NetworkPassphrase , ledger ); err != nil {
171+ return err
172+ }
173+ validatedNetworkLedgers = true
174+ }
175+ if err = MergeLedgers (& ledger , generatedLedger , setLedgerSeq ); err != nil {
176+ return fmt .Errorf ("could not merge ledgers: %w" , err )
177+ }
178+ } else {
179+ ledger = generatedLedger
180+ if err = UpdateLedgerSeqInLedgerEntries (& ledger , setLedgerSeq ); err != nil {
181+ return fmt .Errorf ("could not update ledger seq: %w" , err )
182+ }
183+ switch ledger .V {
184+ case 0 :
185+ ledger .V0 .LedgerHeader .Header .LedgerSeq = xdr .Uint32 (cur )
186+ case 1 :
187+ ledger .V1 .LedgerHeader .Header .LedgerSeq = xdr .Uint32 (cur )
188+ case 2 :
189+ ledger .V2 .LedgerHeader .Header .LedgerSeq = xdr .Uint32 (cur )
190+ default :
191+ return fmt .Errorf ("ledger version %v is not supported" , ledger .V )
192+ }
193+ }
194+
195+ if cur == ledgerRange .From () {
196+ firstLedger = ledger
197+ } else {
198+ if err = xdr .MarshalFramed (writer , ledger ); err != nil {
199+ return fmt .Errorf ("could not marshal ledger to stream: %w" , err )
200+ }
244201 }
245202 latestLedgerSeq = cur
246203 }
@@ -377,8 +334,10 @@ func (r *LedgerBackend) Close() error {
377334 defer r .lock .Unlock ()
378335
379336 r .done = true
380- if err := r .config .LedgerBackend .Close (); err != nil {
381- return fmt .Errorf ("could not close real ledger backend: %w" , err )
337+ if r .config .LedgerBackend != nil {
338+ if err := r .config .LedgerBackend .Close (); err != nil {
339+ return fmt .Errorf ("could not close real ledger backend: %w" , err )
340+ }
382341 }
383342 if r .mergedLedgersStream != nil {
384343 // closing the stream will also close the ledgers file
@@ -425,7 +384,7 @@ func MergeLedgers(dst *xdr.LedgerCloseMeta, src xdr.LedgerCloseMeta, getLedgerSe
425384 if src .V != dst .V {
426385 return fmt .Errorf ("src ledger version %v is incompatible with dst ledger version %v" , src .V , dst .V )
427386 }
428- if err := UpdateLedgerSeq (& src , getLedgerSeq ); err != nil {
387+ if err := UpdateLedgerSeqInLedgerEntries (& src , getLedgerSeq ); err != nil {
429388 return err
430389 }
431390
0 commit comments