Skip to content

Commit a8ff882

Browse files
committed
Tentative: bump iterations updated_at on prepare to prevent race condition.
1 parent ae98956 commit a8ff882

7 files changed

+43
-2
lines changed

mocks/scenario_iteration_write_repository.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package mocks
22

33
import (
4+
"context"
5+
46
"github.com/stretchr/testify/mock"
57

68
"github.com/checkmarble/marble-backend/models"
@@ -36,3 +38,7 @@ func (s *ScenarioIterationWriteRepository) DeleteScenarioIteration(exec reposito
3638
args := s.Called(exec, scenarioIterationId)
3739
return args.Error(0)
3840
}
41+
42+
func (s *ScenarioIterationWriteRepository) TouchUpdatedAt(ctx context.Context, exec repositories.Executor, iterationId string) error {
43+
return s.Called(ctx, exec, iterationId).Error(0)
44+
}

repositories/scenario_iterations_write.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"strings"
7+
"time"
78

89
"github.com/checkmarble/marble-backend/models"
910
"github.com/checkmarble/marble-backend/models/ast"
@@ -170,3 +171,18 @@ func (repo *MarbleDbRepository) UpdateScenarioIterationVersion(ctx context.Conte
170171
)
171172
return err
172173
}
174+
175+
func (repo *MarbleDbRepository) TouchUpdatedAt(ctx context.Context, exec Executor, iterationId string) error {
176+
if err := validateMarbleDbExecutor(exec); err != nil {
177+
return err
178+
}
179+
180+
return ExecBuilder(
181+
ctx,
182+
exec,
183+
NewQueryBuilder().
184+
Update(dbmodels.TABLE_SCENARIO_ITERATIONS).
185+
Set("updated_at", time.Now()).
186+
Where(squirrel.Eq{"id": iterationId}),
187+
)
188+
}

repositories/scenarios_read.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (repo *MarbleDbRepository) ListLiveIterationsAndNeighbors(ctx context.Conte
9494
}
9595

9696
liveCte := NewQueryBuilder().
97-
Select("scenario_id", "version").
97+
Select("scenario_id", "version", "updated_at").
9898
From(dbmodels.TABLE_SCENARIO_ITERATIONS + " si").
9999
InnerJoin("scenarios s on s.live_scenario_iteration_id = si.id").
100100
Where(squirrel.Eq{"si.org_id": orgId}).Prefix("with live as(").Suffix(")")
@@ -113,7 +113,7 @@ func (repo *MarbleDbRepository) ListLiveIterationsAndNeighbors(ctx context.Conte
113113
LeftJoin(dbmodels.TABLE_RULES + " AS sir ON sir.scenario_iteration_id = si.id").
114114
Where(squirrel.And{
115115
squirrel.NotEq{"si.version": nil},
116-
squirrel.Expr("si.version >= l.version - 1"),
116+
squirrel.Expr("si.version >= l.version - 1 or si.updated_at > l.updated_at"),
117117
}).
118118
GroupBy("si.id")
119119

usecases/scenario_iterations_usecase.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type IterationUsecaseRepository interface {
5353
exec repositories.Executor,
5454
scenarioIterationId string,
5555
) error
56+
TouchUpdatedAt(ctx context.Context, exec repositories.Executor, iterationId string) error
5657

5758
UpdateRule(ctx context.Context, exec repositories.Executor, rule models.UpdateRuleInput) error
5859
}

usecases/scenario_publication_usecase_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type ScenarioPublicationUsecaseTestSuite struct {
2323
scenarioFetcher *mocks.ScenarioFetcher
2424
scenarioPublicationsRepository *mocks.ScenarioPublicationRepository
2525
scenarioPublisher *mocks.ScenarioPublisher
26+
scenarioIterationToucher *mocks.ScenarioIterationWriteRepository
2627
exec *mocks.Executor
2728
transaction *mocks.Transaction
2829
transactionFactory *mocks.TransactionFactory
@@ -52,6 +53,7 @@ func (suite *ScenarioPublicationUsecaseTestSuite) SetupTest() {
5253
suite.scenarioFetcher = new(mocks.ScenarioFetcher)
5354
suite.scenarioPublicationsRepository = new(mocks.ScenarioPublicationRepository)
5455
suite.scenarioPublisher = new(mocks.ScenarioPublisher)
56+
suite.scenarioIterationToucher = new(mocks.ScenarioIterationWriteRepository)
5557
suite.exec = new(mocks.Executor)
5658
suite.transaction = new(mocks.Transaction)
5759
suite.transactionFactory = &mocks.TransactionFactory{TxMock: suite.transaction}
@@ -159,6 +161,7 @@ func (suite *ScenarioPublicationUsecaseTestSuite) makeUsecase() *ScenarioPublica
159161
suite.clientDbIndexEditor,
160162
suite.featureAccessReader,
161163
nil,
164+
suite.scenarioIterationToucher,
162165
)
163166
}
164167

@@ -482,6 +485,9 @@ func (suite *ScenarioPublicationUsecaseTestSuite) Test_StartPublicationPreparati
482485
[]models.ConcreteIndex{{Indexed: []string{"a", "b"}, Included: []string{"c", "d"}}},
483486
).Return(nil)
484487

488+
suite.executorFactory.On("NewExecutor").Return(suite.transaction)
489+
suite.scenarioIterationToucher.On("TouchUpdatedAt", mock.Anything, suite.transaction, suite.iterationId).Return(nil)
490+
485491
err := suite.makeUsecase().StartPublicationPreparation(suite.ctx, suite.organizationId, suite.iterationId)
486492

487493
suite.NoError(err)

usecases/scenario_publications_usecase.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type ScenarioPublisher interface {
2929
) ([]models.ScenarioPublication, error)
3030
}
3131

32+
type ScenarioPublisherIterationToucher interface {
33+
TouchUpdatedAt(ctx context.Context, exec repositories.Executor, iterationId string) error
34+
}
35+
3236
type clientDbIndexEditor interface {
3337
GetIndexesToCreate(ctx context.Context, organizationId string, scenarioIterationId string) (
3438
toCreate []models.ConcreteIndex, numPending int, err error,
@@ -69,6 +73,7 @@ type ScenarioPublicationUsecase struct {
6973
clientDbIndexEditor clientDbIndexEditor
7074
featureAccessReader PublicationUsecaseFeatureAccessReader
7175
screeningRequirements ScreeningRequirementChecker
76+
scenarioIterationToucher ScenarioPublisherIterationToucher
7277
}
7378

7479
func NewScenarioPublicationUsecase(
@@ -82,6 +87,7 @@ func NewScenarioPublicationUsecase(
8287
clientDbIndexEditor clientDbIndexEditor,
8388
featureAccessReader PublicationUsecaseFeatureAccessReader,
8489
screeningRequirements ScreeningRequirementChecker,
90+
scenarioIterationToucher ScenarioPublisherIterationToucher,
8591
) *ScenarioPublicationUsecase {
8692
return &ScenarioPublicationUsecase{
8793
transactionFactory: transactionFactory,
@@ -94,6 +100,7 @@ func NewScenarioPublicationUsecase(
94100
clientDbIndexEditor: clientDbIndexEditor,
95101
featureAccessReader: featureAccessReader,
96102
screeningRequirements: screeningRequirements,
103+
scenarioIterationToucher: scenarioIterationToucher,
97104
}
98105
}
99106

@@ -231,6 +238,10 @@ func (usecase *ScenarioPublicationUsecase) StartPublicationPreparation(
231238
return models.ErrDataPreparationServiceUnavailable
232239
}
233240

241+
if err := usecase.scenarioIterationToucher.TouchUpdatedAt(ctx, usecase.executorFactory.NewExecutor(), scenarioIterationId); err != nil {
242+
return errors.Wrap(err, "could not bump scenario iteration update timestamp")
243+
}
244+
234245
if err := usecase.taskQueueRepository.EnqueueCreateIndexTask(ctx,
235246
organizationId, indexesToCreate); err != nil {
236247
return err

usecases/usecases_with_creds.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ func (usecases *UsecasesWithCreds) NewScenarioPublicationUsecase() *ScenarioPubl
257257
usecases.NewClientDbIndexEditor(),
258258
usecases.NewFeatureAccessReader(),
259259
usecases.Repositories.OpenSanctionsRepository,
260+
&usecases.Repositories.MarbleDbRepository,
260261
)
261262
}
262263

0 commit comments

Comments
 (0)