Skip to content

Conversation

@vandonr-amz
Copy link
Contributor

@vandonr-amz vandonr-amz commented Jun 29, 2023

There has been a lot of triggers added recently, and they all reuse more or less the same code, with some variations.
This leads to many (small) inconsistencies in trigger code, and also in a lot of duplicated code.

We can package all this in a single base class and reuse it as much as possible, which is what I'm doing here.

Adapting all triggers to this base class is also a good way to resolve some of the inconsistencies.


REVIEWERS:

  1. I'd advise starting by looking at the newly added file base_trigger.py
  2. Then, look at how the other triggers were changed to use that new base trigger
  3. Then, look at the small changes in the operators/sensors
  4. Then, look at the tests.

cc @syedahsn @ferruzzi @vincbeck

@boring-cyborg boring-cyborg bot added area:providers provider:amazon AWS/Amazon - related issues labels Jun 29, 2023
@vandonr-amz vandonr-amz marked this pull request as draft June 29, 2023 23:53
@vandonr-amz vandonr-amz force-pushed the vandonr/deferrable branch from 23e1322 to 8814192 Compare July 4, 2023 23:29
@vandonr-amz vandonr-amz changed the title WIP: introduce a base class for aws triggers Introduce a base class for aws triggers Jul 5, 2023
@vandonr-amz vandonr-amz marked this pull request as ready for review July 5, 2023 17:47
wait(
waiter=self.get_waiter("query_complete"),
waiter_delay=sleep_time or self.sleep_time,
waiter_delay=self.sleep_time if sleep_time is None else sleep_time,
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 a somewhat unrelated fix that allows specifying a sleep time of 0 in unit tests. Without this, athena unit tests were taking 30s each

Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine, but you can also just mock the sleep function no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think just setting sleep_time=0 is sooo much simpler & cleaner & easier to read

Comment on lines -42 to +41
poll_interval: int,
max_attempt: int,
waiter_delay: int,
waiter_max_attempts: int,
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 trigger was added in #32186 merged on June 27th, last provider release was on June 20th, so this breaking change is OK.

Comment on lines -205 to +251
compute_env_arn: str | None = None,
poll_interval: int = 30,
max_retries: int = 10,
compute_env_arn: str,
waiter_delay: int = 30,
waiter_max_attempts: int = 10,
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 trigger was added in #32036 merged on June 27th, last provider release was on June 20th, so this breaking change is OK.

Comment on lines -32 to +33
class ClusterWaiterTrigger(BaseTrigger):
class ClusterActiveTrigger(AwsBaseWaiterTrigger):
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 trigger was added in #31881 merged on June 23rd, last provider release was on June 20th, so this breaking change is OK.

Comment on lines -168 to +121
class EksNodegroupTrigger(BaseTrigger):
class EksCreateNodegroupTrigger(AwsBaseWaiterTrigger):
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 trigger was added in #32165 merged on June 26th, last provider release was on June 20th, so this breaking change is OK.

waiter_mock.side_effect = WaiterError("name", "reason", {})

trigger = AthenaTrigger("query_id", 0, 5, None)
def test_serialize_recreate(self):
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 probably best viewed in side-by-side diff. I removed existing tests because there is no logic anymore in individual triggers.
Instead, I'm testing the only thing that can be broken, which is the serialization/deserialization.
To do that, I do a cycle of serialize-deserialize-reserialize and I compare the serialized data. It'd probably be better to compare the instances, but at least comparing the serialized output can be done with a simple ==

I copy-pasted the same test for all triggers inheriting from the base, because I think it's better to have it in their respective files ? It could also be a parametrized test with many cases in test_base_trigger to avoid the code duplication, open to hear opinion about it.

Copy link
Contributor

@vincbeck vincbeck left a comment

Choose a reason for hiding this comment

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

A simple nit but overall I love it!



class EksNodegroupTrigger(BaseTrigger):
class EksCreateNodegroupTrigger(AwsBaseWaiterTrigger):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are you making a separate trigger for create/delete here? Is it no longer possible to use a generic Trigger if the responsibility is to just poll for a particular state (depending on waiter_name)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I remember you said back in the day that you preferred separate triggers so that the status/failure messages can be more descriptive ;)

Also, given the lower footprint of triggers created this way, I think it's ok to have specific triggers for each thing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I ended up adopting your method haha. Alright, so just to be clear, I am going to be creating a separate Trigger for each operator again.

Also, given the lower footprint of triggers created this way, I think it's ok to have specific triggers for each thing.

Definitely. Big +1 for reducing the repeated code.

return EcsHook(aws_conn_id=self.aws_conn_id, region_name=self.region_name)


class ClusterInactiveTrigger(AwsBaseWaiterTrigger):
Copy link
Contributor

@ferruzzi ferruzzi Jul 5, 2023

Choose a reason for hiding this comment

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

I may be over-complicating this, but it looooks like the only difference between ClusterActiveTrigger and ClusterInactiveTrigger is the waiter_name value and the two message. In which case, why not have them both inherit a ClusterStatusTrigger which accepts those three values and drop all the repetition?

Same below where you split other Triggers out into status-specific ones.

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 status/failure messages change as well.
But you have the same comment as syed here #32274 (comment)

**kwargs: Any,
poll_interval: int | None = None, # deprecated
waiter_delay: int = 30,
waiter_max_attempts: int = 600,
Copy link
Contributor

Choose a reason for hiding this comment

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

is this supposed to be 60?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's supposed to be ∞ lol
we don't really have a clear posture on this, some triggers have sensible max attempts values, some others wait forever...
What I don't like with setting a "low" value like 60 is that if the user sets the poll interval to 1 for instance, it ends in 1 minute, which might be annoying.
Overall, I think it should be a really high value, and users should set it themselves if they care.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think a good compromise is to keep it the same as the default one used by boto. That way, we don't inadvertently mess things up for users who might be relying on a Task to fail after a certain amount of time, because they didn't change the default value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, but in this particular case, the existing behavior was to wait forever, so....

Copy link
Contributor

@syedahsn syedahsn left a comment

Choose a reason for hiding this comment

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

Some minor comments, but overall, looks really good.

Copy link
Contributor

@ferruzzi ferruzzi left a comment

Choose a reason for hiding this comment

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

Looks like all of my comments were addressed.

@vandonr-amz
Copy link
Contributor Author

this can be merged as is only if #32389 does not proceed.
If it does, I'll have to change the parts that are "breaking" to surround them with deprecation warnings.

@vincbeck vincbeck merged commit 05f1acf into apache:main Jul 7, 2023
@vincbeck
Copy link
Contributor

vincbeck commented Jul 7, 2023

Amazon provider package is excluded from RC2, therefore we can merge it. See #32389

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providers provider:amazon AWS/Amazon - related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants