-
Notifications
You must be signed in to change notification settings - Fork 12
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
Alert execution repository #380
Alert execution repository #380
Conversation
app/com/arpnetworking/metrics/portal/scheduling/JobExecutionRepository.java
Outdated
Show resolved
Hide resolved
* | ||
* @author Christian Briones (cbriones at dropbox dot com) | ||
*/ | ||
public abstract class JobExecutionRepositoryIT<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is almost exactly what's in ReportExecutionRepositoryIT
. You may just want to git diff
against that.
I'm going to follow up this diff with a change to make that code use this instead, but the gist of it is setUp
had to be fixed to be generic and the success tests needed a newResult
callback since the result type became generic.
app/com/arpnetworking/metrics/portal/alerts/impl/DatabaseAlertExecutionRepository.java
Outdated
Show resolved
Hide resolved
final Optional<AlertExecution> existingExecution = org.flatMap(r -> | ||
_ebeanServer.createQuery(AlertExecution.class) | ||
.where() | ||
.eq("organization", org.get()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: here we compare the organization, while below we compare the organization uuid. Is this difference necessary? Do the generated queries differ (e.g. one uses a join and one does not)? Is one better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell it's identical - Ebean is smart enough to create the same query. Regardless, I'll change this to be consistent with the code below.
app/com/arpnetworking/metrics/portal/alerts/impl/NoAlertExecutionRepository.java
Outdated
Show resolved
Hide resolved
* @return The last successful execution. | ||
* @throws NoSuchElementException if no job has the given UUID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems odd that we would throw this exception given that we return an Optional
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was preserved from the old Job Repository API. I think the intent is to distinguish between executions that do not exist and parent entities that do not exist.
Optional.of(execution)
- That job was valid and that execution existedOptional.empty()
- That job was valid but no execution was scheduled for that timeNoSuchElementException
- That job does not exist OR that organization does not exist
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have not already, please document this in the Javadoc for each method (I suppose in the interface).
app/com/arpnetworking/metrics/portal/scheduling/JobExecutionRepository.java
Show resolved
Hide resolved
conf/db/migration/metrics_portal_ddl/V21__create_alert_execution_tables.sql
Outdated
Show resolved
Hide resolved
app/com/arpnetworking/metrics/portal/alerts/impl/DatabaseAlertExecutionRepository.java
Show resolved
Hide resolved
@Column(name = "alert_id") | ||
private UUID alertId; | ||
@Nullable | ||
@DbJsonB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is your likely serialization culprit.
Here's the relevant thread:
This change related to 542 may offer some help but I have not traced it all the way down:
ebean-orm/ebean@13f7d37#diff-2a7fe1eac4aafd91195da026c9a88a9fR675
Specifically, here it looks into ServerConfig
for an ObjectMapper
, if we can set that before this method is called to the one we created in Metrics Portal then you would get all the goodies we are used to having with ObjectMapper usage.
If not, the fallback strategy here imo is to not use @DbJsonB and instead perform the serialization/deserialization in the repository object. The only downfall of this imo is that the schema may not be easily upgradable to @DbJsonB in the future (unless you can find a type that works today with manual serde and in the future with Ebean serde).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the pointers!
I went ahead and configured this in both MainModule and EbeanServerHelper. It looks like it did the trick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Apparently it works fine in the unit tests, but I'm actually discarding configuration when trying to use it for integration tests because I don't load the same configuration that play does using the hocon file.
I'm still trying to figure out how to override ServerConfig
from within Play. If this takes more than an hour I'll probably go with the fallback strategy mentioned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused by what you mean when you say integration tests discard the configuration.
There are two styles of "integration" tests.
-
*IT.java
code is executed in the Maven JVM against Metrics Portal JVM (e.g. via its APIs) running in Docker. The configuration loaded by Metrics Portal is the one packaged by default into the Docker image. -
*IT.java
code is executed in the Maven JVM and loads and runs classes from Metrics Portal against actual dependencies (e.g. Postgresql, Cassandra, KairosDb). There is no configuration in this context in the Maven JVM (nor should there be).
Instead, you will need to have the tests inject the correct ObjectMapper
. You need to be careful because you want all Ebean tests to inject a single and consistent ObjectMapper
instance.
We actually have a similar problem for working with the Play APIs because they use (the same) custom ObjectMapper
. The code for generating this for tests is here:
You should probably re-use it with some way for Ebean tests (e.g. BaseEbean abstract test class with @BeforeClass
that does the injection). Shoot me a text and we can talk specifics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These integration tests are the second flavor (only one JVM, no Play APIs, "real" external databases). I understand that there are two kinds but given the context of the PR I thought it was reasonable to omit any specifics.
What I had meant by "discard configuration" is that because of the way this is now set up to configure the ServerConfig
, this MetricsPortalServerConfigStartup
class needs to be loaded by ebean in order to call the callback and populate the ObjectMapper
field. As mentioned below, it has to be called at inject time otherewise the ObjectMapper is null and everything fails.
In the tests, we load the ebean classes manually from within EbeanServerHelper. This sets the ObjectMapper to something reasonable, but if MetricsPortalServerConfigStartup
gets loaded here, it will clobber the ObjectMapper. This scenario is what I mean by "discard" - we overwrite any configured value. I was accidentally doing this in one of my earlier commits and it caused my tests to fail.
It sounds like then there are currently 2 (3?) locations where we create an object mapper, and they all vary independently (even though they should all be the same):
At the very least I can make the last two the same since they're both run during tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please! Reducing the two in tests to just one would be very helpful. The duplication and matching with prod is already called out here:
Feel free to file an issue to refactor the Prod code so it can be used from Test as well. That would make it just one definition. Although to be clear, the risk there is someone modifying the prod definition and the tests passing BUT not being backwards compatible. The dual definition should at least catch such configuration changes to ObjectMapper.
So 2 is better than 3, but 1 may not be better than 2. ;)
This was being loaded during test start.
@@ -73,6 +73,8 @@ public DatabaseExecutionHelper( | |||
* @return An internal model for this execution | |||
*/ | |||
public static <T, E extends BaseExecution<T>> JobExecution<T> toInternalModel(final E beanModel) { | |||
// TODO(cbriones) - This should not be static, repositories should call this from an instance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is not in your development plan to address with Alerting, please file an issue for it against the OSS project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #384
/** | ||
* Plugin class to configure Ebean's {@link ServerConfig} at runtime. | ||
* <p> | ||
* This is necessary for configuring the Ebean server with dependencies constructed via Guice, for instance. This |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: try to wrap comments at 80 characters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After seeing this I thought I could just set this option in IntelliJ. Unfortunately not only can you not set different margins for comments v. code, the ticket for this was opened 7 years ago.
In fact, another was opened 15(!!!) years ago
I'll just do this manually...
// In some cases we manually load the ebean model classes via ServerConfig#addPackage (see EbeanServerHelper). | ||
// | ||
// If this class is accidentally loaded in those environments, then injection won't occur and we'll silently overwrite | ||
// the configured ObjectMapper with null. Explicitly throwing makes this error appear obvious. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe in either case (setting it to null
or leaving it alone) this mean that the ObjectMapper
is left as its default instantiated value, such that any code relying on the custom behavior will fail in weird ways. In production this could even cause data corruption (due to serializing this incorrectly). Right?
Should we be more draconian here and say System.exit()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's necessary - this exception seems to kill the process on startup because Guice fails to instantiate the repository. The dependency chain looks like:
DatabaseXXXRepository -> EbeanServer -> ObjectMapper -> **Boom**
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ Please add that to the comment (or something to that effect)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Just a few questions.
Introduce a DailyPartition manager actor that runs within the DatabaseAlertExecutionRepository, periodically attempting to create partitions for the alert executions table that was added in #380. Notably I've introduced another EbeanServer which uses the DDL role (metrics_dba), since that Role is necessary to create tables. I've included unit tests for the actor while the DB testing is covered by the existing integration tests.
No description provided.