Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add DRY_RUN config option #97

Merged
merged 2 commits into from
Nov 26, 2024

Conversation

gustavogama-cll
Copy link
Contributor

Add a DRY_RUN configuration option that controls whether the service executes operations or simply logs the received events.

This is a temporary feature needed to help test the recently added logic to monitor contract events using http connections without causing any side-effects.

@@ -112,13 +112,17 @@ func (tw *Worker) updateSchedulerDelay(t time.Duration) {

// addToScheduler adds a new CallSchedule operation safely to the store.
func (tw *Worker) addToScheduler(op *contract.TimelockCallScheduled) {
tw.mu.Lock()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these changes to the scheduler.go module is due to a race condition uncovered by the new integration test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are other places in this module where other race conditions are present, but I opted to leave as is to avoid adding to much scope to this PR.

@@ -55,7 +55,7 @@ func (tl testLogger) LastMessage() string {
tl.mutex.Lock()
defer tl.mutex.Unlock()

return (*tl.messages)[tl.NumMessages()-1]
return (*tl.messages)[len(*tl.messages)-1]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is to avoid a deadlock uncovered by the new integration test

jkongie
jkongie previously approved these changes Nov 18, 2024
@@ -449,7 +451,9 @@ func (tw *Worker) handleLog(ctx context.Context, log types.Log) error {

if !isDone(ctx, tw.contract, cs.Id) && isOperation(ctx, tw.contract, cs.Id) {
tw.logger.Info().Hex(fieldTXHash, cs.Raw.TxHash[:]).Uint64(fieldBlockNumber, cs.Raw.BlockNumber).Msgf("%s received", eventCallScheduled)
tw.addToScheduler(cs)
if !tw.dryRun {
Copy link
Member

@RodrigoAD RodrigoAD Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this changes the nature of the function, now a flag completely changes the output of it. It can make it confusing and hard to maintain

The goal of the PR is having an option to avoid side effects right? That's not on the Worker side, but on the Scheduler, so why don't we create another type of scheduler that doesn't execute?

func NewTimelockWorker(..., dryRun bool) (*Worker, error) {
    scheduler := *newScheduler(time.Duration(pollPeriod) * time.Second)
    if dryRun {
        // This could be exactly the same, except the execute, which does nothing
        scheduler := *newDryRunScheduler(time.Duration(pollPeriod) * time.Second)
    }
    tWorker := &Worker{
        ...
	scheduler:          scheduler
    }
    return tWorker, nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal of the PR is having an option to avoid side effects right?

It's more about "testing the event listener". I'll give your suggestion a try but I have a feeling it will be harder to test and maintain in the end.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look good to me, how do you feel about it?

@gustavogama-cll gustavogama-cll requested a review from a team as a code owner November 19, 2024 05:59
@gustavogama-cll gustavogama-cll force-pushed the feat-add-dry-run-config-option branch 2 times, most recently from e080275 to fd8a997 Compare November 19, 2024 06:06
RodrigoAD
RodrigoAD previously approved these changes Nov 19, 2024
ecPablo
ecPablo previously approved these changes Nov 19, 2024
Copy link

@ecPablo ecPablo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Might be worth to create a ticket as a part of repo standarization to add t.Parallel linter rule to make sure unit tests are running in parallel as I think the t.Parallel()

}
if !(reflect.TypeOf(tt.want) == reflect.TypeOf(got)) {
t.Errorf("NewTimelockWorker() = %v, want %v", got, tt.want)
args := defaultArgs
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be worth adding t.Parallel()

Copy link
Contributor Author

@gustavogama-cll gustavogama-cll Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opted to leave it as is for now. Parallelizing integration tests -- which often rely on a shared data store, such as the geth instance -- can get tricky. It might even work as is, but I didn't explicitly design the tests with parallelism in mind. So I'd rather take the time to think it thoroughly if/when it makes sense.

Nevermind. I got confused and thought this comment was in tests/integration/timelock_test.go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@gustavogama-cll gustavogama-cll force-pushed the feat-add-dry-run-config-option branch 5 times, most recently from 86207a0 to 0838722 Compare November 21, 2024 04:44
@@ -153,6 +157,48 @@ func Test_dumpOperationStore(t *testing.T) {
assert.Equal(t, wantRead, gotRead)
}

func Test_scheduler_concurrency(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the deadlocks I saw with the earlier version, I decided to add this unorthodox test to validate that the scheduler can simultaneously add and consume operations.

tw.logger.Debug().Msgf("scheduling operation: %x", op.Id)
tw.add <- op
tw.logger.Debug().Msgf("operations in scheduler: %v", len(tw.store))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This len(tw.store) call is causing a deadlock, so I'm removing it from addToScheduler and delFromScheduler.

The alternative would be to refactor the scheduler implementation, which I've started doing in this branch. It seems to work as expected but it's a risky change which I'd rather not worry about right now. Once we've enhanced our test suite I'll give it another go.

Add a `DRY_RUN` configuration option that controls whether the service
executes operations or simply logs the received events.

This is a temporary feature needed to help test the recently added logic
to monitor contract events using http connections without causing any
side-effects.
Instead of using the `dryRun` flag to control whether or not operations
are added to the (standard) scheduler, we now select the type of the
scheduler pass to the timelock worker service:

* if dryRun is false, use the standard scheduler
* if dryRun is true, use the new "nop" scheduler, which only logs the
  calls but does not do anything

In practice the "standard scheduler" is a new type + interface as well,
since the existing implementation defined a the schedule as a simple
data type which was associated with the timelock worker via implicit
composition (though all the schedule related methods were defined on the
timelock worker type).
@gustavogama-cll
Copy link
Contributor Author

friendly bump on this @RodrigoAD @jkongie @graham-chainlink @ecPablo

Copy link

@graham-chainlink graham-chainlink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not super confident but LGTM

@gustavogama-cll gustavogama-cll added this pull request to the merge queue Nov 26, 2024
Merged via the queue into develop with commit 4fe06c5 Nov 26, 2024
5 checks passed
@gustavogama-cll gustavogama-cll deleted the feat-add-dry-run-config-option branch November 26, 2024 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants