From f812c372bea3a08e9d77700c52c20f14600e4405 Mon Sep 17 00:00:00 2001 From: Kaituo Li Date: Wed, 16 Aug 2023 17:06:00 -0700 Subject: [PATCH] Update Gradle Wrapper to 8.2.1 for Enhanced JDK20 Compatibility - **Reason for Upgrade**: The migration to JDK20 ([reference](https://github.com/opensearch-project/opensearch-build/blob/aa65a8ecd69f77c3d3104043dd1c48dff708bffa/manifests/3.0.0/opensearch-3.0.0.yml#L9)) rendered the current Gradle version (7.6.1) incompatible. - **Actions Taken**: - **Gradle Wrapper Update**: Upgraded the Gradle wrapper to version 8.2.1 to maintain compatibility with JDK20. The gradle wrapper files are generated using the `./gradlew wrapper` command. - Applied `spotless` due to new formatting requirements in Gradle 8. - Resolved test "jar hell" issues. Gradle 8 introduced internal JARs to the test classpath that conflicted with dependencies from `org.junit.vintage:junit-vintage-engine`. As a remedy, these conflicting JARs have been excluded. - **Relevant Pull Requests**: - [Alerting#893](https://github.com/opensearch-project/alerting/pull/893/files) - [ML-Commons#892](https://github.com/opensearch-project/ml-commons/pull/892) - [Security PR](https://github.com/opensearch-project/security/pull/2978) - **Verification**: Successfully verified the changes using `gradle build`. Signed-off-by: Kaituo Li --- .../workflows/test_build_multi_platform.yml | 2 +- build.gradle | 40 +++--- gradle/wrapper/gradle-wrapper.jar | Bin 61574 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 16 ++- .../ad/AnomalyDetectorJobRunner.java | 45 ++----- .../opensearch/ad/AnomalyDetectorRunner.java | 8 +- .../opensearch/ad/caching/PriorityCache.java | 29 ++--- .../ad/cluster/ADClusterEventListener.java | 16 +-- .../org/opensearch/ad/cluster/DailyCron.java | 31 ++--- .../org/opensearch/ad/cluster/HashRing.java | 10 +- .../ModelCheckpointIndexRetention.java | 12 +- .../ad/indices/ADIndexManagement.java | 6 +- .../org/opensearch/ad/ml/CheckpointDao.java | 56 ++++----- .../opensearch/ad/ml/EntityColdStarter.java | 20 ++- .../org/opensearch/ad/ml/ModelManager.java | 8 +- .../AbstractAnomalyDetectorActionHandler.java | 22 ++-- .../IndexAnomalyDetectorJobActionHandler.java | 32 ++--- .../handler/ModelValidationActionHandler.java | 16 +-- .../opensearch/ad/task/ADBatchTaskRunner.java | 8 +- .../org/opensearch/ad/task/ADTaskManager.java | 117 +++++------------- .../DeleteAnomalyDetectorTransportAction.java | 30 ++--- .../GetAnomalyDetectorTransportAction.java | 50 +++----- .../SearchAnomalyResultTransportAction.java | 20 +-- .../handler/AnomalyIndexHandler.java | 65 +++++----- .../indices/ForecastIndexManagement.java | 6 +- .../timeseries/feature/SearchFeatureDao.java | 13 +- .../timeseries/util/ClientUtil.java | 9 +- .../timeseries/util/ExceptionUtil.java | 8 +- .../opensearch/ad/cluster/HashRingTests.java | 4 +- .../diskcleanup/IndexCleanupTests.java | 10 +- .../ad/feature/FeatureManagerTests.java | 6 +- .../opensearch/ad/feature/FeaturesTests.java | 6 +- .../indices/AnomalyDetectionIndicesTests.java | 46 ++----- .../opensearch/ad/ml/CheckpointDaoTests.java | 7 +- .../ad/ml/EntityColdStarterTests.java | 8 +- .../opensearch/ad/ml/HCADModelPerfTests.java | 6 +- .../ad/ml/HybridThresholdingModelTests.java | 6 +- .../opensearch/ad/ml/ModelManagerTests.java | 11 +- .../ad/ml/ThresholdingResultTests.java | 6 +- .../ad/mock/plugin/MockReindexPlugin.java | 8 +- .../ratelimit/CheckpointReadWorkerTests.java | 4 +- .../ad/rest/AnomalyDetectorRestApiIT.java | 86 ++++++------- .../opensearch/ad/rest/SecureADRestIT.java | 7 +- .../org/opensearch/ad/stats/ADStatsTests.java | 4 +- .../suppliers/ModelsOnNodeSupplierTests.java | 4 +- .../opensearch/ad/transport/ADStatsTests.java | 4 +- ...nomalyDetectorJobTransportActionTests.java | 10 +- .../ad/transport/AnomalyResultTests.java | 4 +- .../transport/CronTransportActionTests.java | 4 +- .../DeleteModelTransportActionTests.java | 4 +- .../EntityResultTransportActionTests.java | 6 +- .../ad/transport/MultiEntityResultTests.java | 19 +-- .../opensearch/ad/transport/ProfileTests.java | 4 +- .../ad/transport/RCFPollingTests.java | 6 +- .../ad/transport/RCFResultTests.java | 4 +- .../indices/ForecastIndexManagementTests.java | 37 ++---- ...ingleFeatureLinearUniformImputerTests.java | 6 +- .../dataprocessor/ZeroImputerTests.java | 6 +- .../NoPowermockSearchFeatureDaoTests.java | 5 +- .../feature/SearchFeatureDaoParamTests.java | 6 +- 61 files changed, 409 insertions(+), 643 deletions(-) diff --git a/.github/workflows/test_build_multi_platform.yml b/.github/workflows/test_build_multi_platform.yml index ffc2aa8a3..bb72ac18e 100644 --- a/.github/workflows/test_build_multi_platform.yml +++ b/.github/workflows/test_build_multi_platform.yml @@ -11,7 +11,7 @@ jobs: Build-ad-windows: strategy: matrix: - java: [ 11, 17 ] + java: [ 11, 17, 20 ] name: Build and Test Anomaly Detection Plugin on Windows runs-on: windows-latest steps: diff --git a/build.gradle b/build.gradle index c0278088e..74ae2511e 100644 --- a/build.gradle +++ b/build.gradle @@ -64,8 +64,8 @@ buildscript { } plugins { - id 'nebula.ospackage' version "8.3.0" apply false - id "com.diffplug.gradle.spotless" version "3.26.1" + id 'com.netflix.nebula.ospackage' version "11.0.0" + id "com.diffplug.spotless" version "6.18.0" id 'java-library' // Gradle 7.6 support was added in test-retry 1.4.0. id 'org.gradle.test-retry' version '1.4.1' @@ -160,11 +160,12 @@ dependencies { testImplementation group: 'net.bytebuddy', name: 'byte-buddy', version: '1.9.15' testImplementation group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.9.15' testCompileOnly 'org.apiguardian:apiguardian-api:1.1.0' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2' - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.7.2' + // jupiter is required to run unit tests not inherited from OpenSearchTestCase (e.g., PreviousValueImputerTests) + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.2' testImplementation "org.opensearch:opensearch-core:${opensearch_version}" - testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.7.2' + testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.8.2' testCompileOnly 'junit:junit:4.13.2' } @@ -302,6 +303,14 @@ test { excludeTestsMatching "org.opensearch.ad.ml.HCADModelPerfTests" } } + + /* Gradle 8 is including some of its own internal JARs into the test classpath, and there's + overlap with the dependencies org.junit.vintage:junit-vintage-engine pulling in. To prevent + jar hell, exclude these problematic JARs. */ + classpath = classpath.filter { + !it.toString().contains("junit-platform-engine-1.8.2.jar") && + !it.toString().contains("junit-platform-commons-1.8.2.jar") + } } task integTest(type: RestIntegTestTask) { @@ -711,8 +720,8 @@ jacocoTestCoverageVerification { jacocoTestReport { reports { - xml.enabled = true - html.enabled = true + xml.required = true // for coverlay + html.required = true // human readable } } @@ -722,10 +731,11 @@ jacocoTestCoverageVerification.dependsOn jacocoTestReport compileJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes,-serial,-try,-unchecked" test { + // required to run unit tests not inherited from OpenSearchTestCase (e.g., PreviousValueImputerTests) useJUnitPlatform() } -apply plugin: 'nebula.ospackage' +apply plugin: 'com.netflix.nebula.ospackage' // This is afterEvaluate because the bundlePlugin ZIP task is updated afterEvaluate and changes the ZIP name to match the plugin name afterEvaluate { @@ -735,7 +745,7 @@ afterEvaluate { version = "${project.version}" - "-SNAPSHOT" into '/usr/share/opensearch/plugins' - from(zipTree(bundlePlugin.archivePath)) { + from(zipTree(bundlePlugin.archiveFile)) { into opensearchplugin.name } @@ -766,9 +776,8 @@ afterEvaluate { task renameRpm(type: Copy) { from("$buildDir/distributions") into("$buildDir/distributions") - include archiveName - rename archiveName, "${packageName}-${version}.rpm" - doLast { delete file("$buildDir/distributions/$archiveName") } + rename "$archiveFileName", "${packageName}-${archiveVersion}.rpm" + doLast { delete file("$buildDir/distributions/$archiveFileName") } } } @@ -779,9 +788,8 @@ afterEvaluate { task renameDeb(type: Copy) { from("$buildDir/distributions") into("$buildDir/distributions") - include archiveName - rename archiveName, "${packageName}-${version}.deb" - doLast { delete file("$buildDir/distributions/$archiveName") } + rename "$archiveFileName", "${packageName}-${archiveVersion}.deb" + doLast { delete file("$buildDir/distributions/$archiveFileName") } } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cbfa754578e88a3dae77fce6e3dea56edbf..033e24c4cdf41af1ab109bc7f253b2b887023340 100644 GIT binary patch delta 26632 zcmY(q1CS;`vo1WgZQIt4ZQHi(cgN;Cw(Xf6+dH;BJ2rNVyWctI|L;8!9bH*ji0 zd@?Ja3f~7$xB-V$mIH^t0D>Z5CgGujVk9xSfc{qv83F_Z#L3l)864#Q_E^XK??xa5 zC?P0DGa0BO+&@JUywiWFrT?eV2m$wBqYpwf*uMe-xdGyzK5!4Wc?R7d^q;u>Pc+Ar z{3kY7V@W{$)7%l9|GV$(<2L^#j0XRw|4DlPpRsNfn*YW&PteqXVPk-Rz`*=_BoPq) za(Fp7Xb=!@R1gpWpa~`+5Sa)XICDq~8Pe?7UxJd_5yu2|~_=yWmbXC`e-Tr8O8x(zdt)iZ1s^ zXT*Y`b0c!6_()7xz{Ccba97RRV&N!!I5C2BK;eg`9)HI2t$mzHOaHDIwTAG^XxEY? zCrY`)|43^CP#-dVtYXuqdPYscy5glEPdB5Mm@dpK<<(bb5$-V$X%ONoA z);JlOSAQV_3EtsVxK5N$n19fOg@s=D9(fdNrfWMS zp)S56N;Ut9i8Uq%aXXm~$tFrm9;U0*XFTw-<1%~IZu5mF-R~cjnc$3ofsRd`ZQUV` zEzh9hzwF|*JgOr>nd|%Me_=!UY9VqRk(wV%!aYx)wm0G_RH{rh_2r&yS_gJPS^ps6 zL1_G?TEycFsK1{xQrij|EDFuB&m?>jww2~K2-3SKAMCO(D~z+o4bV_qhMgiCeYtjY z;NjV}j}No#5_ls@*GJU`7z&wXH$%I7g&dnJsp|G!8s0cl@a8Pxma;J)(YP^sDYtP$ zmlh%T?aKocW%6m~k(xvY+0pV+Z*%(7g+6oWXSUCeEPm%5@weR1jSj&PX}S_Bih+af z*p*(k2WAfh)#eQ@PMsAPGX@R}J%BwlEpMA_ZWuDT?D&cd#Az1;3P2g*(uy?-Cjnu> zsqG>IRKpN>OjnA0uq!uF7b5Cf5St1z8(N=wsj96{ci3%aJt+Hgk(PW*}s9Lwj2?@-Gj^Oq3*V1vWdrv<}12wvu^u=R+ddk zpzaLAOnW*i3CTEh)?vFDxqc%#!&5+Zzs`NeIT2sY>x+t#v9&=-a8^>X(v~<6Drkcw5mA%yj4)) zCq{P+I^2$#@mKuw1K5F9axjn}M(ss-FapfBu~YRHbrix8&^~yfTHufsmPgIe22yJy z$sI0yC_iCfeD>(b+F95Pxps8kdaTRU0~x*ADZI_$DDbUX*p)HLGU6*;TgKNVHAEOABhJuhs|n3O@0%O&Pe6x}H$#by&{i3j7M_uF~J z&BlnU_QMShV4v#=-=3_O*|phe{|T$FVT6(k$4InFXeDzoapWH7HPga~xy1W=tHu_! zV+Y~Lm*0vBqkQMdUAk*jb+Mo~&PrsS4IyK68(JeasonlOXrO57(c zP&{?Mn}bfVn8FvU^s^ilPfVx91YzI$5U0d?5r=FZQk+5P=Kr+Mj6*=7n5Yw1I}0KbV(#5_Ka ziWd_M4YN|2H!2Mk9H{wPIF*8V4vbwdXFbs$;-OpIbSrmU?5r^X)1X`vjXut$h%4Qp zwS_^9G)o+Ix&Lrrz`Q9i$PrHWp8X;6%Spfu5KOcczWKWiu?4Bn{Ekf5%fxK&%5&DQ zAnj7-u>G6_tKkS13>upCf}!di_b5Mi%FC3C*vS8oKmTzeLbJB2m3^lyrT5 zzb^jt6b5Wf?)Wg(l)DR+lnRA1#Y^q}WXyJ`L!|XCODptz)}d`J^q*x59g=sk-c(3l zKw{JW?O4T~7uKjhq)_?TI#1ml)~1-$qA`PPgq6$wt^C~l{5DUJ-Fnb#OS|G8f8C9Z zHQreAA$H%x@{*!*Y?HrM`Rlpw8mVcmP~e@OZkpA;133c8j7c6(g2n;y8i`;Cso6*s=H(OpiZBlODaJ)9jg%|7T8yQ`}}ktIq!b{hIZ{f2RBAWdQqa@%~4;wI%<5V4Zk| zOuh>o1SARy1cV)UXG;K3HgUIBcQx^}aCI}W7qhl7v$JqzGP5^vb4yj%)56!l_zH)x zgb`y_YlNhe^bL~V#Pt=CD{RS7tu3SF5-yc6h;Yuz@nl1v&H2gydXP^7=ua1{K&F*T zf4fd_?UpGhT z4@VoNP&o!lb+N+({LH^)8j*l8G7%g97bBC_x~4E%IZCQd!%WHCb399J6&cqaHkr;H zeX;}emJQMjsXu;QtZ%| z^CLN#tY+%ui;=9KrP0gxEL<)+HGkLYFH(mYnIZh{Fz#8{!&<*LFB~SiiNBPzp=Qz+ zeG{RIiL7{GkO-;}+P-HCKD7?nyF9J!I9ne*$BcD7jzAnhtTQ4`ISS7RyOBgy&IG4& zn7#w=W(Kbsz7HpCL=uHzd89jT(R+53!xf6kNUaweqI)oyQpaY=yCSWR{=ql9nA?LX zZz7sU4n!wy*~BW7L2CqQHo0_bQj$%Nx=1HiB%$eH<07Qi3Zo?onoILGEIiPXzT6^#Zx5V(nn;%Z+;&NUY zHp5pEzV01dE!hc3U6cE+4%^w=Wuo|XzbL(u!N#&$Jf#HZ1ijTYH)yaOZ^2&+t$(!axLY^xAP(+l|4j$Hl2~{zX>ImyTEH zkZ4Yt$!FWt`)k2u(~CCpHZdA*o+;f2%S%DEO(B8j4m5!Ftq}0Z;@&*ZC&HcYDJHj_ z&JtKAM!Zw*L4q1jE?2$HMS=}%K|Vm3@g#EUmv=|M27P;SIp;3n%5i`5dUPt;S^$MI z9Q*(*env5`*%+o!I2d+Q%(Ih&9gzz2-^V6zjA>R!70tv9F1i zMait<$|4nqI&tr}&p5+NJ+b=#EPiKM6e7#@b%_*|Q~_ll-(ypp^YTK_qD`}V%s(?_ z+Z-|fsr$ntn@@OFG-3)l!aT4qrTjQ)tUk zT%F^?)}4~FR@~WE%HA*ZUnc4Qcj+Z@(y9f(KtQ1X1s#I_6L(kvUu`h}TCOI}&K9oX zHg4{&Hl`l#HcpNjuJ&S1j{mHJ`~PPSa@F*d&_yu5%COT`j>wcTQE4Gt7{|$aaqQ$_ zY(-V8Y)Zq&&e+lhEIiqB(H^z-;VF_|Y!95+M%nd3izp}@n@*-W0#4Yh0zcl~5c}!! z%#%iHLPW7(r*r^pH^u|^&^je|bIC;^$sue*{ce>dJ_CsiX;}!Bf1RaRV%1C0eq9I3 zoqBa+CR*0uz42SSGS>Q+$!8j@n|Ny?dTiR+^`gVn+nMjlScGcw-|pBENr?8P5Efg+ z*cWar4ER9W;eRQrAW^kaK8oD^2~RqY38Y}zT;*lNiNq@s;(RyiqG zEG{<@g@>EGxH51yxbYS{>=6r}_o93LVx9qIYKXuKe<$YlabK~0S}tTJi&=4Lkai`H zZ#ly`uD5Yp4`!EN>6#c1m`@>;DX^0b3m;2FQ2S*6lk{j?SmhC7%$SUc&ArFg{z<93 zog5>j{bdH=vD*oYu&^_5xx=YOQ2Ks$#gSx|d@B1z?Vt6i4nU1lnz$W$_|5VsT1y|l zR-=qjerOl-oueko^sK6SJhzL3Y=07Y0Wir)nbQ@>oYlkl?-fQy`dT5H$LHJ{jKHRl zl21`&h+HK34Fo~o(Xu0=kcKC0Vo^N&kZ?v64sZdnCOO^qw^k*I6oxz!rha!2zo$#h zN_x#cFwU;na-kLX2+VG+&my~%S?_U1K-fcp)Wf_*@Gfco$gt?+BNkZB@NKkiP{4LE z0GOA|511!(pNJCLWL5J%sgtwz>-W%&YJTJosQqM1vv`KBj;PB|Az$q0Du3-WyD4Bpgwi;RQqx&gD`Z zOcdK-YHKXV&EvY~;bHZe7$nbBsGrTT_Q3P!kM;|*%`pLDg`iZc4eMw9f)(3*81D$5 zDu}w#jSB9?JK=RN*F@IE$Omyt>VUE)k}n1cP+TFW-;rJrNNiE^a9TQ}?u2ErfKc4qyn*0% z^_-GvOW*LsoFW@bphfph4h!+=Z;P%$2H84;?yB6h_BTnUshi|NyF(@Kt)18ux{K7m z=AVZ>qkC@YYMY$Yv>h@7%~qSxgMxgkToAB73V9Al=_Smm3`Jji>8flZ)h&X2V%AP) z%&`TIx&_r!vTwqhIWWi9_42TR0vHJ4ozpLBA69-+@qbY>CFI*hMLd|eM;VIKTc`in zCFxb41-(h7TOY94*+&rWl9ZdSnEfp!zA8E4nt(57r1IV^e53hzjKEf&zQf5;KtY$oTBA&i~qmB>x3(H04yX32mB*6KHsH2aXrLEDSMy4*OB0}` zrS+UCLjijSW|y0}?w8-w_j%R){9EYz^9!e+_Je}_XGtuuoyLSKQVxx2GGnM4ff$RT zn~{`~JXQi%%Nd|C%GphxMu{s@Vxd2V*vJ6wq9(nANJhS&7Rrj7tDSU5wBt19;7_tY zM54ZLt-+wv_=mz7H-O9+#}4ftVWQi_nZB~Rb=o0(#b$ag2N%kSqjhReDT{Z;nWYr> zL$S39sXTrDxE{b(@g&MGO_#xy-erJ;aI-K$ zLW6xF-j;VO%M@nqAk~DWGr6#hB^FFG@%tkVB0%&b{kX-gai=AM5L_3&7HHy%L z?K;DNk_P@a8+U{MD%ckFt;UAOf6--8G1E34$Z~nb(hh8IOsP_dnuZj;ipD`&9rlzU zYnfwqwE}EUu|6*0X!|Ri=uq#Ujo)hvxaf`0`Q*kqmP=WV_lC4j>=>xWV6yvovQNgu zTjI5bdeBX0S^iPA{XJgiHmOj@ywR^mjvn1>rT9vqzwT5}Yq-QP=%A~I!RC-w!+J#~l^iqn{=lUX1yIzJ|~&(+ns%AqzhfcPAU#N43h8V(L1IQg4*N znqijUD5jgS!BEp7BknZONE7;S%;N+upxwnq>6muAk@LdOCgwniVJ(o9^2r0hrl6s4?#JX;h&cFLW7BK$%k&?9Sa;c z#B|h3#o`DhhfJnh;OIOq)=$5_svTtN36_CcyQNaumzcGL&Qthu4;b8d)egh2m2a-o zS`2FM3~GZu;QPzLUdJ84ohCxV3a3}=8bIU4lWtzCz8a&Wa&P0gdzGZ`1XGd}qSNXs zW8H%A>3AB$e#P6i4)zKV?v~3dSpiB-=@EB-!`-)DzO2x4i7Qi+?ALh%-*b+tub=5G zp3Sz>;(XM6EaC%`6Ax?Z;av{88tQz1ZL5?jx5IVZfUssaI|2tn*RfS~s?)+p17KV4 z3QMfZay7P%E7#q$xxMpz8`oAOR=`DL1h0(3V3+!g2pb^C!un=SSX2)ofGYq_zG>XI zAO~+)_G=k4oK?R10GA{hM*vrnMOL#V^RHMDpa5f6WHPm}Z@vfKi0aStcaZQXw$e?3 z2mV5S7mTI!QrcZyDrx$>B3x-~Jivh<I`kSxDYdxwKdWp z=^R-hFS0u&=^fTsMz%TZ;jlEk58VN#??Y0)jQrdziI7k9K8GRYwuAdV0>F?w*|2`@ z@N9=$GW0Us8HC+C zMzzS<(Us&KHOCG98{*Oi6sr~TWc$sCWI{M);c9;&}2Xzj|x_AQoMP7utAETgCl*{zKO!!nvUop9%{ z&2HPboc#$IXsd0)G0*7f49=c+8Emm?eg{wGk*wz)1!QGPBwnv zBY3G=p?Fh|&trxIjAv?5R$GgXZoB?Ydt5VSUvOOw$2hLHXvxo_edA|~0EVhq5vJ}V zEVrMOVy28S2(sK-b*f!l+XJ!c_rD`0D`hnCT9cjL{^~joxmR@$&LzP5Vo^k2?}9s)m2)ukbB9zj zGz?uIs9@FAptW<4I!}_0uCicHz`ZA@F|bbZQeM-XI*=Mqi>A^pxIBLAIkGXOb}f4a zEDDz3{wVTpFHPU#d;P;Wz~+i$B?^LfIwAm`%VSjlzYR50Jl2mXk5Ubbn2)b5`nw;7 z3HNc!zrbW2&>v7^t&N$+OjAoeXUW6tkt7LTYzK-XBv6&|y z3Z~5xUFXP;B%;F*N2bljO07MK%`r)I9D;~s#u%0uoBNB6tsv$m{D?R$()3)EFdF#Q zruYsw9rZ}0?Q4%6^!UP*($W;OCwImvY(}mA6B5;&PFEX7rK~U{3>Ntj_=Pjr(Cvth zmi{{xAdEDZ<=Y(ej4t{JX$hs5_xbbbky-gGJdfxS;aa5hZrx4XnjHoC6V5>+<6Bf- z-pt%T%o!#)l}zWa7`Y~@xHbmtN<0zP7>t~0j)!TMhm5Dqf}FL2CwqHriaB?PN)yx{ zTlqZv@?UV2s22qezP)%UM%*+fX1>10*2s*kfbE?ORp7asKAGh}$s73_#7Rjfa<285 zq+ucF&t-SjQifjP&DggeWOm!MPZRx3_g(Sr#KmQ`YTg} zz4iTXe+(;LBRCH@?k56xcS4s!`FgTx9#g|sYla7IJ1+F2LBBL1&(59^TBx=c^ z(_}wP@cQ<+&7OpRh<#>i?zKo%y+p}=YSEFt3D>H_dORJ7x1#fMxO!T7_GRY2T6*5B z{@d0^*kh1|iZ|hq9nt|0@Ybq4De^|Xp+5r$CqC#7VvW6MHzom8fvD)m?j>h2dmX}2 zx$DOjpbV76Cm=lTWd%Xh(K``ev=<(thcf}TtEe6jz$I4cPbTtc*4f(jqhuv~J7m0u z8VywNsP`cM83{RrkjnV+M>Mhay)+6jrFqvs?pM{EXN-}Y*_vn)s~Atow+yA+SVMFW z8`*!i-m}p;0R{lRUJJP0zc??5V@ZYsSj$xu^1X}mzk85quD_Zdt$zCt{gqYlOS@pn zku8mR9_A)HkrrwbisT=ro4A>2l_hIv3{#t#e$P8ffyE$AyveGYMnSxd(U7?tI}R`j zmpu=nXUUnW2DXg0`3Sa_+OwAUisIQs{6 zA>4V2g%7QB8H&6TC?g^Tabni~c7c-#zx=*{4mazfZgD_xH%}%_QC%9V%s&3J%Be$H z95x+I_Dcbv#L2BON%|X$0+afcjwf8K*^Rv|4Og+RYPXCg>U^YwjFD4Hfq%1uafQ-q{UlRuO*o0i9v})pp(qI!P%y&S!wq}M5hUqbdl>`$A0qt=wOII`YFDlkAZ&hcqf_nn#s_R@635een?hu_2ei=i3VS z{y8Q>fW(QDK@pO8K+oO+*(wAG*^CTVqeT3n#;HvbM}n_TimiQohn4b-qTv{O$pM%C zb$|+2kG!b$_6`2Of87Oel6k&=DOnB%-~fOTuq;HKIpvqxZ4WLE1u6xJpp!}`7mSJ) z3`+sZ4$D!nAa$P{D{r}=qiw&u5$)M*S{kF<)|$q&U`mgULcbxQ*R>wd7NeJ5zj9qY z{Vep{v!2YlaPuD67btwab+qNz`ptW?1FQ6LF%S`Djr-k`LD#V~WTAg8QZJe%YtxMf zn7vQTw(>g_$x|ED@YjVUJn2|?;mFexh04D!gqTg=2}a`^h36C$v$UMd478a^IcA8P zeNx6`P989|NrMS-bFWJ?kn^aUZ(k9E@0c0-yG0TcN3=0|;(JQ(pEo+`9+(<|b@$rj z8ZwO{T~5Ipg#2wJt-s(bLw zWlmFxh{!uuwBZ^*!{@wSXx|jG>lhQW^9DxbVLHyjwORQzbEHczUKF12?ClF)r(4F% z=n1q$uVN;BRFcr`Yd!po$lG_|b9%ll_0PO)*SBO^^^TaSpI=X5B*eSHsD&;C;3khI zTWV*a+DMkd+WLphbcv59&X^HxjG5$;^Cet-=w`-6Lz@!eowU!uB7cT>U35zw_OSg@ zI-&On%$3|-e)&t(8(qKhipP71h-XxNhwl*xoLN2VHafZR=oPX5Yq|f1mAhN@u<0Jy zHN$=Wft2gC@cPrfTj$Wr)*l`Sn8!uZn{Q3TjnQE(V_(r3vLwSW!d^#5l9(!-o*$Q9 ziXQ0zZEmz$T*)KU+wcR=&Gr>u@d)eKv`5I? zPZ|~G$3sZqU{7t87;J3Ejdx-dgOxi8k&jvVRd2MpyWCb>-s5ypwiEXQ@W$7*$^G~F z>h${Z{2ZaB&EZ@J+xTEcw8)JvLss{FwchDw8})Q+CBD+ulx}mY>+E3XN!VCQ5Bd)c z#xrY&SW*%y>dRxDw0nsQg)LH8&8UEioBvicmuIC!WQoa^S-g%s(POLjyfzD(Wfe|- znRh_1o8gQiPX}2m@+WHz;3o?a#zoquq|9(g;i#hg z8KhN8SnW%mw7Zs(^Hem&fpUNc3qfof%P-B@g7->-K6iLvH6xpO)a5|KG7x9eM&f-c zmsC!l0GC5dZ^~MEbf$z}5HGY0-jWT=-0^)^c7?VmmkS5>Cfo%au*eXg=#j?C4nHkd z?o$B65`R5p)%D3sooXfALcFQ9kqt0y-!~;~LxS#BWh>?0;hQ?z=ccUo+tS z@Q8coVMF4GbUi5Oku`d=@Uo597g71Xgdu35Y#>k^@|7#igBh>ZP1gIl2T=a0){-3Q zGYwc!q=UKs{C1)NP@echTsS*JR65#1s^GRY=tfSMX?j--u|@dSu2R+DfK@JlawY4V z0i4QnlbtbIr)F#1hYt@wdkiX=O+w+2AVSw=rQBE+);cN!ZVEWBYcQ#bM9gQlid7Iz zj~kYW5fyaK2a`e7;)?xsl94zAnb}0&gv}uW#vAEuPcfnZi)1y(;S7|0mPm+EM=CSn z1?DW5YXuDH7@QOywpKij5<_J$X^}efzsS?n#FJb-slp>lg)!}&M&|d2wd*NOQ-;Z1 z*q1EAqz;O8sCC3DX?H;U%Fp%nC`cJHj*n^b%;wnH<$}idnCP$7a|nj2;M7w!&DF>R z^CH1v(|E%HN5RHCW3>BIMWb!D{@J5#b{~RqH_|`A19f>Y!PP!nNvy~km`V+c`IX1P%itNTla1%k3?d& zaZ7a7zF0#DW~WPF{|Xn|Td}T(1!I)RDO$cX;drhC94*1^i61kqVK{IX7ZxZ_|1O$n zjU&@fk~r055jkn8Av_OXE5+KY{DGQI;}jB=h}-tIn8rijm+!i)nFe1?pDq|L=Hd~= zwUcv*(VJd4>0d@ zk~g^oNaRj0yJ1{(LWcBfoJ5bLC<;j%W}tJA?i8^`?99HNe_;x_Irc-~lPBgrHT0Fv ziXgPPI^>&Lqlr%mlR^v>0tg=>kGr?(BL2N_j(8Yj`vo15Xmmkdz zpqieQI4&=Jj^&sw3eue=<+r54Zg}?{5&PhBl-wucP5Y8z-cKzcvA=!zfG-1l>a9LK zxj*S&-Xugt5(Wfhe^yPrxJ||i1xrQmA`J1f+M!^R0LqeKfa*3+X25S#OvEqJVJ(h! zc2W++uW8arbr#sMV2});uF8seF$)E_`{8pCv#HK zMxz6moeM|Z_-PgLJ$RW?6TYno%AS*?mN9X)uWczAGmLlXdnUqcwV_UV`8d)zI>HM0 zd!XI?*2Yn7cWd=y_5bR|9Y)(3m?TW&(47lOW zf6E_#x?J;3tDOQKUOebN{iQjET&Jhb%gpp_YD`yR2i-jBWPZ>;gnK4j;lW9oWjdiX z86EUa>Ceb%tW2b^PZhS%PWCfUALXQtFuv2;^US|W{})a zVwqmhC1@KTTM51QLMtLP+cxyw>`;`Czi$<8s%FXodEwC7P?Zun-9%VImF};^$7(9iuyoKp^&Xv6ofcZpfWq^Jp6&S&L}t)bB{4R`AAI@Qy(<-)m8z z0MJ+T4KnIYTWl2H;ddrxjcn4AyY(k|JxjN)T1XOdtim~1|G8R$Sn^_kdj3^i5_?W+ zuDbE}S@9$5U%g3JN~r2Q7}Ww_GTA~?_xvI2P>$qXH%&{{ti!B=qRJXgRmfV^nwd84 z5*-adnU2Q4i?61H`mMux!sLx-r9TC}k?g=xP~3GCiAMutVMPp>uu3$L$wcXvn(V@P=1$63hP5G$ zt%6*ua{oRMz{*&tH1{bxIQ=N|h9T&2UI2hjaf+`(ha;$>sJQ%vZ0rDL=kUijsvD1R z@$`j)kuqHpd4oI-flhfl0JxUYrEJZ-x4YY53BDJ4KPuL0a;ob+(Fa0PtzHai`-qd8Ul0b)$uD9>Kn{?sWup{j zo-P-xDr1!O&)!#+`hbO;2!5r^_(TX;feo5up$;ah6Ogo`I;*)3PV1z|{e)q(b$!*} z8IjRdRl5))$qP~8l<(k>Y2X&Wr-X%@!vU5J3c|u0Wl42h=@xQ!oyh z)ZgbN%mh;y>>0!>CTq&iO#maTJ%DzW=Tz?YlGxtaSGi=r&w zcpy1#CP%7S66piQ>Ey-N1$@4U$v_!}7W5=;m|bL(8yZh!^38Y>NXSK;w1E8L*&!NO zT~Irrx(^?oxSvChkZ)w|fD7Rx^hZjZd2pvdTtOyvsBnD4BA2+{!CJgpT^S<{NvlpY zA){hyLV{0t=wY9?BRhtxlFENmlv`aX8|F-QMtI5koOkZ z(>2b_OUd8<4dH(6mE$gMz;}q+ptDt^pDq^=zy7{#Ir{-8U0QiM0W#?6?=2Lya=hIF z%R0=8eeIPMO<6<>+3vU=49^(#G_-~)=k)hyGUo)^hWgIf2A;886gG#o>b}96h~8aP z74PO6zjF>gkE4^TRR8MeYVc+Ijz%Nzh(M+Bza#HRJ}l+@D#ExtUNNn!`orVksn(;! zoTHXgB8Q}~6JM&c1K6Q}^p{9Tx~7*^HTpDb=@FFqjHsxWK@X>Oc7XI3AJXzM%7s?n zfI!T#r<(;7P(i;g=iQFyYB;MfBrJsV_z;u9NbLQk1T5NBVbJhStinjabhT2 z1rOCCeXK9D?1Zv;K$PZKB~#sv1oI;_?jSA%2$1-IUzKr(01g;m-aS+9@l}Ex!A_A# zg0>Khi6Fl9&ffPPG0^50rFmw_QG0$?_s2XENxRao&N*yJw>`YNL)Vx5@ZsE^#wNvu z4c>t-kv@3inpy1U71=Gk0W(v9n0R*fWTk*Td<+5kCDC;X{U}Bkq+a|;{wP~GhD|z@ z5Y&iWi8^qX0w|F(1d3AlB9YBEqw4fCKBB+TVv0g}K4H1Af5rwP!KdD_VTwCq@+qnQ zrqE8cRj-`qipyI$#f{5bKIIJs(rLMvVh_5Y6|$gX(pNQCQ;~M3r6aZF%baF3OV6O$ zwLpS?d{9)0N(h3TAWP55i~deOOu|g){Lt-}xz`$)0jTnvE6ufn>hrX+XUW~U;RZ5| z%_4ntTLw>&8zd9;g#1drS1))^n)N)U_((807UY9Rd5v__;(^x{lI&A`{7a{|Ub(Aa zZ@Xqd(r4p*KWD-ta<3h)6WK5h&ZWlT7N>-nie2UrPJ6MQl z9!J45>|K}HSI}iPw$&-H2^grL;Ou(*l3TDLx&Xw50a6VQ9!(E_JAMQ(%}6RmIl8C!zxKeq+uho1RzRhZzDPhno`8KIRuEN#q{|f;G6x%DJhemZyD@@EUwY6o zuwY~K74IH~Lh)sP{mg@BcRNdXr1o~k;A%aollcA3{t4M*NRic7M2*wI>TrRdYEbHX zRSxKz-z6>9Zcl@w8t7FXzyWl?B3;RZiS1`TiEU55S3H-&}$`vh@%N zqU>D0&01nF_jz=O7OC)=q*LD5Rdymjg}L>-O5zB=#D92BstN?*|3snvDY^e(2f_`Bm!p0iRStwYWB*aRQ*UZLIGcml(b@E?el3-YF6fFq2(k;`CRm zBU>%SR2llLGy%%R)hi{$EWLz3u3ZJVh1LBG4qFcbEzC#90oeircQjHl7Jlde2lsU_ zE*Lb3kgP@~C~hN=F#@}^e&cQN!~3+_ORz$fm@RbYPtcoSZgQ+>fY01wET;O5y%0K3) z{v~enBJk?pxsm39tfBvKQNwc2{-1QEa~~rc=RcN5Iy?vn*?*CDzAB*s6s-Tl9MwEi z!_Y$c7VtDNCcqpZ_eP^M(FuY~5I;yFksbyqnk3E2n3@qxPcpG&XQ$7~c<;R_srlp9 z`zMB1yFxqF|4!|zL_Jydd1~+R-Q2!x>D%VRBO5djJ}$K)0v zgcS3grN&r1EVvmCAgQrrl9A-r{lh4RqTWm%*NSDpIC%gP6Fft8J|qT#X?b6Vf2?bg zE^iEes)$`qHY}(G?zb+Ka~LukT$#pYP-QZ*7UmR91I85GlF?!<)+DR@MNuXp3nOiV z$!S%ff>MTJ@Le(3MFzElZ(!dU90!w0UFO7VnOQ>DN>d^u;B)xpRRnEg*go9Mh?SjJ z%Ste)m2_;Hf~_vE)!cMuxmo9oifZUreM;&dyAPX1yEC*k9U1GnC;#oHjGGLb>@QeQ z?p8cAANkY<%2ndEu~yh1>wI8YiUR?g18;J+f{d5Ek_#GsI8QlxryZLii6k!-sKZ>V zOlGNH9)aWvyV48dT zX&~CxSK+_T?fUU`uP!!}+R6NGV-EAE-d&n{vpV6JF#Ix6J1Y%PKLHu6Q-&d)w>p~0 zL%0}65BAu!35s9q8GDw+pi>I|)r+1C@iamQh>K;D__M*cSd1!z>$Qa+)P?=K^DHdT zkwVB%Ezj)8pSS9LF$HIvr9?`sE@o8Ua{92EKbCXrA$Z-9YqK|N-Bl*uQsxO{QnSv& zVmNxXVG)U0wz9BLFcCqF-Lh+`m9LhgXgYIcumB$HF0f z&!X^t7k^>JU(T8>GPo9ZMk$q2is#m2Izz(2hjtfCPa3SMVP06AgkYtqg?T|YPi zNb*Keppu4h_4`>=aYUd_PM2jDHrc~(SmTLVgYsFTjH1j?(}OQZP$@eId4h{Yh;Fd% zafXn2BsnoCwl#PJ6d$J}KVsnF3^5=D0A@o%IHia_U<>58<^jwWpOq{ZA^{zW%Xj=l zy#|fmy5q_$oi*XcPW=G1fPsw!wSjezFyBZ|MDfj-+xmzKCE-uv|D6SDG%sKD{YTry z1`?HX0sawylyOG$n?3013HLrmnNw)ZNC=5dBTZu|B!Pw4>~go)H3X7{EzMU;-{bg9 z97)z3cMI^}G>3kQ@c3p>X%;uUPJVg2@7)6BpAXMt%pmaET!|7o;h6+sI=I)9?ut?p z@efe0+H;NL#vI_iXVKVSdB!-E*SF7GuKpJtfWv4hcaxYOjz3T?$Pj#hnivO|OwU=G zjBo_?GS!dfm?5tV@Z{H>cAJAc8kN++oaqsTFu0m>)XR$+Le{#Cuoq(edW@*ru%&AX z#e5rCeKHb~)$!DZzn<~Pyod1XT#q)lY9IQOd|Xv_e{j@BIBb@Mnv}YC4~bM+5&y_T z2MCyCTn0VbR4G~(J4DbSsSY>~cEaQ^O4+lO$SpeI-v5f+?7HYq<*;yGmT~D-HOXMi z#bwf|Oh`pC76H?Q0(m5g6Pqi7s6|5H3k*gwnM`GhLDo=x*bHJcpP#wQ0>YrDJFgGJ z%w)18ahn*g4SQ_OH4d|QND5Ka?NuQl18_$9W(>JKLkU@9zln=mE8<~&^Iab|N|h6l ztThuot*2BJuZ2&5O_bIg;IaARjt%$Rr}?Sp{3A8pLs{*@ZtR9(G0`UDRPU%0HOMB< zIHaDyWJIb^#EAutc-7a!`zg?t`%BOTqj(lbI?!VgGeOsyFw`eSQfgD&Q5QsI06VOe zEmgL{d_tbT>M?HIyR`H%R(p9 z_f$Y=bJG3>BxwqB3n?^lF*J${2zhM;ZHwe?ieg})I9F7LNXla8+U-kQyXSyc-Cwr7-yJv8l2D;v=LuWSI|efXliv)_@w0vJrPPtuNv-p8 zl-;^nP&RlyOTQKP>RkXRefK5l`u>_kovX5o4AXozGfxZfTL(v)jt>im-G*6+eEEl^ zV2z+?X0UFz8fk6z)EE5oc21tO&JX1Od! zA8q zzz>1$Nx2vwX|7)J6FC9@Oq7nTAuaF9ne#rje8a=%lSSqi@CE&&{p!+pJY*w!H=)!0 zi~CpK9Y$hqX}98tXYVs3f8Y$aU!}s!3pYU8*D0I&lz)F;{>t8TCzvaAQ7Y(| zoB(aUd}4kOLKG-{65$azJe1(;-4{!Ay8!ZUSDo40hYg@|GWLh}vQ&g<{Qk{)(5qvgBmD?k!^GHa7ZnyqwL&Rqxbg zEcQA2OCBJ>hz;clVJjNsY@{GXhK2CF%o=AG3De_j>PEt0?fU{P?>n~f29L#P5Z?Ag zg<^AVd|(Ov?}+jrB#F1TDV)WYH0hf9GG?q(*i6nJWJpXnfOZ4nQ@q+7%UghDLcCaLuME3j(&pZ-&Db$L@co|$ zwN%()WoAquyw4NHJQ>;}?t$wrfB1SSZl<3;rsZeCr4xR06jI`FZ9+kJPOXiCK#lMx zT?<>gkzl1bbuDI|A`jLqx?S6Yf^PYBnOTyUa6uiymSOTzu9*s$TCIB9mXO-ak{uwEN7E7q5GVrj!jQ5#v!@@9m@ zy(Z_k<>hwNvfajN29Y{h=5b<6xs)a?E2i6^itiC~L6I%h(?t5hdMt~>9}XE!Hub z!gy^jLphNkV&StbmAYAvNV+irWjH$X!%u+1EV`F&IVzWJ%UsuX{SJ;mo+D)KW(#t>yrwS%@V24k5f`EzbU>it(+k`-&vg6bC@96%!+= zohHC$U7KqMsF1iWvKICrLR zSe)X%c%itvyKB+n4#lBp@rB|pi_7BfuEn89aSFw)xI>WwrSG=)dCq;#^LwBDkezEX zGnq{CCCTKU)40JEfccp)Q)*c@7p;$J0XcC8hHXi`tOAWgAlOOM>s_yMu}k>!2E$0P%Z12ik;O^y>6PH4A*E5b(tdOaRB>QoH+g09V9|K#b)H6wMAXL?>5`NxTKt+ zKery7p(pOC2jFVCrkT;>apJ%AFpo^XR}UE9gl0eidRS;#C)KN8n(;{^N{MW;ZRr%3 z^oU`d-xcOv9zVtPonO%xxHs1%=GR{w_Yqvmw=Fo^WF|mZ^r2d{`4tM_H(S_Bq~@xn zV%R}p*(aRbrD4P9xON?Tu=%o)W7Ayj#8Uk+gJJA$#INOSs^bvNQKZH#D4J5|yOqCy zc-XMYc1bc{h76B72%zaik~d#2hZK=H(pX8;OEFEOqF2i^@is;3N7krtHjOUhNnP7& znu$7Q=BuRrY{@O2PIEl;F23}BhC~2$sJ4+)k>LlP#l7fwD%e9!q?BJPo%g)q<@<2<8k zCU}}OPPIxr&ROdD%_(W6H5(q?ssOv*`WX^E`5VN`e%0CSpvGkVJ}3N}r^I!`%BO`( z8&BId?$t=$RJTcSQ)I6-vxiP)u9MgBhy~j~=)tQr$ir3_VexCA*bIey9>-76wJZ71 z-jBk1WCGf-CoD7h+Xi!$6f*mYQ$;XO z6$*ohJLd3}%vA(^PZ>0I6e;G_J>15`svlO^=x~7z9hz@XTI}cat*{a<7b`!weh#eB zCekl{V`cM2Uu9NBo4qQ22q2{adIl~7TrxxT+xs-(kJf8UzhbnMS;T2~T5a*@J#}_G zB2m!~l8&db@-)dXdfG;#J4S|0Spm#U;u!nl^p;&uxifmGqNlX9BkNuYVwILzWqLnt zt1TX(ITm|%& zas%A+OmekfsgA~2W=Iuux=BZ$BC>6UhArgdb4>{KX&`@bch${?FWxHv_l%5wG^){{ zV_(Ul%?gYeVpBpr^suYyS zYj92B;j7dXRy|+pCrq9Yd)WK@%UIE}W7G2N=X2$Fb3q}$wlxH=@i2>*ZLgR$P__GB zM3%vibEsCxG`Ic8%^03u>E&YY8Gjs&9dU2eChal#pd!RIN<{Gl6yuv5X(iySy4u%9 z(?y4^FwEUr$5=XELAnTIQC<|^qId0B635r`iqOB^GlYl)Kvg@G73X}6>^tVWk!tGi z;*;v{M)#4teZyY8)LrL2JYWFEyH+z0-jx+2pCjEklu z_3f|LrAW)WnaEMlLA1)STM%EPd(hKbm(5)vy$c<^;fL^5jnE_oyJf>&&|ni2UN61hbsGOz<6$)I1Wi( zgvgmVf;Q}1g}#;94apEu!-^{Wp2y+N38%m$1fr=)7vS)II5}!WOGx}Ab(*);-VwOo)Z0n))B|6rql`f)U zgQqf>g!yj^RUqasSSX>>jz&Yf8Rld{Dyh3|RG+~whIkfu%wE>HH6j1#^Rj4o3TK5A z={1ApUdBR}>o1#>O65~!9CtY`<}@?>P8VZcA?6=idX5HX*A{oK7ROBhl?kzLV)!rtV$g`pfYG7eaY$^>*xn86S6~HJeG1P zL=7T;X#rGjXfaH8I+Fjnp2eY7O4>!!fGfa7nJ?Hbx0jvm>y<<&6>_KCP5b~?j{!3! zy{bX!7KRt6sKGv3vNPVVb33sEFTK4W|JeaG18$n8Wh^bi0rb{{rF>U0YfD@{g_XpD zzh`J0b{s@82t*&{=CrgDxa*Z!aQw&?8^f^}$&-OP+R-dbFli`+9jB71LNl)*E%@Vr zydy3BY#>ecZs!pU+_1WT#`71v`3Yo9%3|vx9NkUx5tYX*-yisn3>iv)q(7DOg)up& zBNVvIHV~DZyc+m+#Oz8q>qcE9CJVWW`b1ybbp_(MO4SjO{ed)(kt-*ZvTBoN0X=t= zFAWOE=LXvy;#Cx~nHH@UD3}y|ikQ#37(7yGn}z=Gil4K`=E;?9$azOy%|@AV>W}xx z|L_7XzTr{cvCpxgo9~!DylltNl(24}+I4&LCPrAHkW`huA(l zy@!aaN71B_Xdh`hEj-b{f6^>d_t=%$tuyV=G+@^FD!J`0>1cElzJV^JLVP@@4J%8$k8JLcwU_IZFJ-)hlP)PGY zcaS{5-FVGDaYXtOG;?B>8GNeBkQtq3#z>a08K2yCPWtf*#*0kO{?H*4BT`!EG$EQE z#>OKtt%GA(Dd4D3-iD@!#|KQjw_>IPLx3+EKm#kt(Z(K5=`K+(~D3eY_`>?Kr z^v16@U_|)Zx>CDQ@{Yf=d^VJTytmC(HQV$1l<~w16jp2;Db$Qq9fD?}oW8IF)T_>I zqy`;2!)9b4`Xw4SJO##^a&c_pegJi~3~;lR>*Y-%r4Xdh+z9q8c&A4RF@=|}LOv`; zRWYOlmIva>iYja8X{|9VR0H!7nw@fe(+L)iu5`c6-7Wf5@l7S_)79mqaUbAdRSL>0 z4V8>D_YX7)yA9hO;D4`@kG%?zCkO-lyFkAfy|%x& z6U=u)Y6gzhbV1Y$C&N)BW_u#K9=OVWTx%*6A1qc2V%9JQCW|M=B2=r4l{em;VT&_+ zWpu*@8zGkZ#u=+8Zbst4KS8Z@*A}CZ?M-=s*@`TuiZ4|CmKd9pq#rAUTjxof2LKDX%`l$_Ei`D2-pt_U7qi zqgTn2@RpVq;}`KdF7Z8_z&5`Y?B58!HHPnd-28kuM{EgsYnfHV-iXZ^P}NWEC6Cgm z8X5Q$5%>r_+2wx*`owBYO*vi;gKy*7F6;$=ZnMxVu^oD>1lE4lD_0V(raHC7Zz-)- z@Xq`e*4Sb>*7}CoP5`7j2F<0RFI;*Vz4($+XC#xc$=W$_jTv`RJ>QN3l8aCxieyX? zA}0(VBc{BPzyUXsak#pWlGGX3#;1k-6E&6B`ok#CiFU`Jh^oior3Nb5d34mjXG**&uX zXg_@%Df1YOp7MaFf^MrtmC1Ctq_kB)+H&nsZJR0voA0B7n`ThOv$F3rx~KH&Ow)Ot zF9nM2CsXYWyPUt%ES0&7rDws&+H05TmP1lAc?;QJ()XhyD2pGlN?$<$r4U#?$P0vI z354iGku*5OW{?*o(i-mKBf)lVa7;V8KqG~~wz>4p%WvVBdFO)SdRo5Z>?X)JKQUa^ zVJqei?!F_Hl~PFT#={h3@qW0?WoNAGiF`(*iKZ}?1LgNf*5e+aK9Kw*>#CKxeLz|e ztlL+7p!-Qt7QsP~B#PS4iZAe7JlBIQv`*;*E~$~QkpQVO^tP%tnl`k{lRHzLTJA;j z9kQ(4$@YCWc&RQBA!^E_K_m`#o*0Nicc>T)%S6t z2kVBiBK#{Amd16jD0A5YYZ!^m8Xoaf+mMdwbgqVrCx()0SIJEfC^6vX8B0=jNn-iX@! zla`3`n}7_8)4xYrX&|SzDH-0)Ipj7Denj>*p(xoj=B)60c-f&=$g-G$0}%zyv?L}9gp^5$Iw`SxXOVe)h~2HJX66sXcHMVn^@N}wqcKH#3W*1{ z(sj{;w~PUVp;%=Gz#!Yaxu5)@LS!=7y?OK?R4kYmgzsb2l;?SrFZI)=c$6bQHVrdo zBMR;MenLLgYMwJ&MmWXL`!d%*sXxWVy;MqQWd0*&YK}uWjVk^lt73PRBZW%q2qb8+ zU!Q*#f508O{ohl41iZ3-8;eKYGpEv5L6&CYufU!!g$Cz)o2@ z=#wT2;j}qxkv{7ONHjJY?W`w6n()%HBgx;LBrY;xK-miOa+5kJi%!ld9CUyK;AJ7Yx0;;^ebG$MOH?uW>8k!v>+3;4Y8&raPL}@ z$PAmM_m0yz(Y5hMx&YkhILdx{Wl|!XL+Z71x`uA8QhArEwp`V`P#o#GTH<5?FHUP>H%QP5*i_>p-u$PE^hH4RNB#k6$?2)#g0J$Q6~Oz}@)T89%(W`O=x+>xASZ zoX|Q$s5GSO{z`32FzDKtgY~)KC?mXaUE9l8X0fc4%Rlqm97Ft69*W^>QDYGLAn!K& zthDt7vE6o~4NGld^{V8@Rm6DRcqu&+^zHU&zUkss49vs4%8M@YDH3msqd;{?}M+Y_OjYnyhoX;}`-- zHJahCOUisSwLa$A84-AS7yIjNA{=~cpiC*N-)9V*#wlEg6LBh^;=c=cQ|3~7>$dV} z>sK=7c*B1gB|ysGo6?J$Y5Yq4xaU(l{{CXuFQotEm6)9Y!E?U2;H)#WM#>(y-{~YZ zXTZklzOmo1WnKevzwyj^$1oA_{(HJ4ZX%Oz1w|^;Ljj+^114P%gJP2%HRr^!KHae= zv>@69rJ{}n=dmC}7Hq;H19tN@dh-Lh4$yFIn1hno6&Se!XiW}c$gKUgkS?SI&S)jZ zTwMGbecRW5ow2p1Jw5!~p@UKGl||cTXBsesH@ zY|m-WiD#v2otKRM3Dg`LoN2F18{D#8f2*#kvZnpM1$7Q>JN6P`Vg~4bFQ7e9RYnt& zfiHRn0@qS!m1Y)twYRCyRfR= zr#pW-&S>a>JUA5y1@_cKT%V*JV6E-o?_%C zj~Gw9Tu`k^1`!((?$$sYN~sl9-`4I&DL#H0U%YrfXXrE#GplMtA6D@(8f7?p7)flK z5R>Xz3P4S=3JP&?;NJ;x6QQzt@tskGei^+Jwrp5vdgqB+X6#HoLwB@dDg2>9!mWpf z_>4cN2=G2HzAtc7a$4xKP+UNi&Zpt}T8D}KXr)J?;^OL`UnXv75CrpIn8D9}IBe6$ zH`8bHJScpJCoh=>ShJihI>ooSUTD{AZfXFu=E(&aw}Gm~0fKn)iB#9Qe1zU?Y-4+U z^#KR{l+Uo+_n495xY~o-xF0|4gM(fpbPHV^_EJahV zGhiH=oQtoOho3{nGf6e?ffb1~iY(#E&Im~KN>HDV2@RaSD7#F@lHQZhdN~5e5%D=# zSat%8m0$_O$qH|~BLP8`(BhCM!Q_n1%)m;}f&pErA>(XG?1fOq4^m>2TVnJ4z)CoS zv6Mu{*_C($!(74BB2e;)xlv|GCNPi!F=wMqxG*&&Cp?jne@c{H za(S`BsCy-}N+uBU`@=@`2X{$LJYr!zgsSfVUteEg{yPJqHaS)H5al-*FaZn<{qK|B zqzfdlGNdN zGPepiv&7ckr?liSh5T0Dz=8U!n#Ej+-Q1(%`B!KP7`4ikKi2?fXH3}hJ;%BKr$^8y z-;KL<(T8urn6NekQPN$CgE_EW`jW85)Z5KCPyDnXiC&}6-Z>GANEe9M&dKlaoRidI z_>^=5EvVM1%wERfgZI{+yqQ38*Zsa;TPt3kwIQuLWL0;QFzZ7*of-ZVQ|q|BzTSQm zpmmB~6mqfNZOp3^DOkVsPme#Y6JbQhc0S#6VfQMKi%oXqZZ5&WW^cPB31|unv2253 zg-6ansz?_MaffIxD7L47*w!=-{iPW{>q6B1!WRAB^i$twhq&=i)f*>Vcngm;vdQ05 zb>sD>A!waONLz>`o_?&C*Vz|$e*Gx^{Yw%4}^$6IlR? z`Zt!#T1k84G>Pg6&_pB$=g5|JEZ5LAmU<&r&{h(unY+%o_;tq(+{`)#tj>424y1y5 zXu6r7blNM5nnsqE)=KL;apu{Xg)by-krlU1O>&-1gUg6lh4_@kXd=bLB_$;YTFcnh z$}BVUIlG$k#f|zx&G4Z{cJ_8YxN}A7+(mTZ&DgLWryU%#+~)L`A9mN8VU|nFJ zgHIwNYUgZu=98V$$@B$P>>BlwKm|U-L>i|!M%g;_v1n|jOI&GqLuWN+u+82NyzR>` zNvu2bVhFcMESD<}^lnw2yX1#kFNlob8ZU1xlS+6?-i|Aa2k&Uw5D0TCPlmjymajz} z@}L5D05vexfxH^^6XqW1u5jQ>N5AlD|1_UjVS80sJPx4ILF%RO_Yt)v(Bk>lBWD;E z;UyeUY;cE@bz9Do@gjXTp`Sxj!j@RY9Z7J;lBe#f_h z3=W`JD~D6c<%>7qc$;#1$4EPadm1HB3Uyr&XyU_0U0$-_bej`tgu3Vr+U{2CJsI^n z^UmEpoKcEDvDK?fQM9u4>aDTrv=Hp3H3e3Ru#IFpNsF}CrS`}yT&fzp;z^?Fi2sB) z*IC%s*=ht>cdYKO1SeJFoV7;f)2ejtHS#T=SiCK)jH}vSNSbd0E1H*NUd7?zFG_sr zMtq_*n#c^~nIiQEJ&Sa(=YW<{zw8~=J??tza4x=ZnS-nf8?olMcksSffH<{iDI|9n z$rot<)F0!eD_#p>sqZ6;R7p-O;wts5_Agy2W){)eu8e*6^-GgY{Cf#7yFW0$wj~#~ zKdmRbfo@gNRqq)$*#vVHksjC#VWVHTz=Xo@eteicM^YRDVI8hG>F@h?PZbG-&sQ$_ ze&ide<(@i7IsDO)iCbpV46ZR_&5y9QO_{i&(gIRDyh_lB;pXAJ#z`SG>%Ln%|4;~n zQ<3y-AAP_|;q^#>p-j&+!Swk$LYX9qC(eyw0cu^`fe~v*)H4;U55o*!+X2>0r_p!U zf)%6hj0Hl5AouhGRl>Ch%8e3k%&SnlSofh>UBw<<-}-RwJDe|3RGF~ab65{0>eGZ6 z1gd>}=%5X}s1iz5-FH)^wbi=eq7P%YRNoDzhrXsEkOtX|68D3VSA%Kh=+9m#c5~@Y z>k!33!h}PU8XcIk?F*+H)D@}gGTDjMgLzmD&{c`pKy~h+#KZ?879{v*1qhcW=H;xCk3=yd#btz|4y|K-H)bj%A{)4-wch#m5AfLHZkM z)h(`5m%Er))*Bs-NA`?lBK3JAD;@V-v)cPL3~jI9EOqHe>sVj+^VSBSqJwoEuZbOv z=kOMkLE8d!YHPMur^G_`H`&c!G(rs`zF1O;Jqoy*^ozi?@h>@qC87m;Hg(O2GhQvvOJ-h!UJ22=Qb4{ z8ek#jnoXBWR2N}ERjXi=0l-isLa0vJKV`w?0{XGEmmOhT;AO-k14OII8hp4;=1VB{u*+=^DNYYIN~X%H3iJA};<>N$aw{F}g^-5M(C1c2 z;QZ*09h2{nWj>YtsiGino=p@(iw0vnCB8gD>=*8ZgbfD~OYf$60e|701E4KO*kM!W z6-cemd-GKU(XOzK%-7eP6t)z3ARk!)plwN_1?tzw0LOsXQ@0_qciRLBA!k+2t_zRe z{?FSIi{>Ct7gRpMc11PufHNfYmK(<`|H|V^KXBWbCBf)=nAzAR@)$jltf77F78 zkzsBZe9Fusaog-S+32|a4)rXB2NG>KR&Foz5l6QaT|H5@7B+d4C_GM|uBzC^8@rj` zH+*Vb8DU!gVb|5ajL-T4CG<5V{dQSTdXv-C8Hf*`ytbnLVHKliZvVmW8|cSpn#8m; zAtUPo+GC~Io#;Rv{fGC$=xR!^06YNPf6%6F*Ixd9)B*@DTBC=q?S2DLZ?BR5Md7Ve z|3#^+GeIfubscCqv#$1s3j_;oi2PL_w?PM`S~iTKCH|%jv<%q%O|NafftHm7K(NY| zAe6c!1%mUpgrU^>)^Dl6mq4)Twj7iy+qQ+4I6H2*|6NG_{gMSZeMc8sd$~gmSmFe4 z(Nlv3zCrzl;&f937J0yZ{0!joZ@;}BhB8+9p$q{qXy^CS8BDv>fD<9`jF2F>=i6^a zB9w6`@<&B{4-x%8w;uk41R9ZgaUl4`9x7l*{4asuHuI%`(5n*v`4t7tfrIvhpxlpp z)Bt+fKfbWblK*9#T>*&pUr}=fXf@UUY9e5t8h~I3J~E((uCSp40c~~D0ti0dM+M+m zLfKSc;K6UWaOMMQ0IxMz{Xi1>kqrGnwFRHs2!Sy+fA`||IAI-Pz^;KQ4xx{U{Q17H zga4~>ESOUXbbGMz;Y-^82J>I|;s4_#^uH)b_O~dh%>Rid{+FNsCmQkp7%K|WaE4kU zA^87;5`%wj;Qzy)f1t?1u>FsTKmHuMfMxXhUH)4S8jiaQwS6e?T{!&E$yLV=1)Bu8^KkTft-&48<%IN>}htbwkw0g(>8I7~)Kh-B=WZ<52#DDbUzgtcJyC=_Ieyb#1Q2s;uKda%t g7Q_E?zh>z_(nw(Q3jz#&7=D;+To{(L52Ty)(*OVf delta 24877 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 508322917..9f4197d5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68d6..fcb6fca14 100755 --- a/gradlew +++ b/gradlew @@ -85,9 +85,6 @@ done APP_BASE_NAME=${0##*/} APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,6 +197,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/src/main/java/org/opensearch/ad/AnomalyDetectorJobRunner.java b/src/main/java/org/opensearch/ad/AnomalyDetectorJobRunner.java index 59e27330a..8d1274084 100644 --- a/src/main/java/org/opensearch/ad/AnomalyDetectorJobRunner.java +++ b/src/main/java/org/opensearch/ad/AnomalyDetectorJobRunner.java @@ -307,38 +307,11 @@ private void runAnomalyDetectionJob( detectionStartTime.toEpochMilli(), executionStartTime.toEpochMilli() ); - client - .execute( - AnomalyResultAction.INSTANCE, - request, - ActionListener - .wrap( - response -> { - indexAnomalyResult( - jobParameter, - lockService, - lock, - detectionStartTime, - executionStartTime, - response, - recorder, - detector - ); - }, - exception -> { - handleAdException( - jobParameter, - lockService, - lock, - detectionStartTime, - executionStartTime, - exception, - recorder, - detector - ); - } - ) - ); + client.execute(AnomalyResultAction.INSTANCE, request, ActionListener.wrap(response -> { + indexAnomalyResult(jobParameter, lockService, lock, detectionStartTime, executionStartTime, response, recorder, detector); + }, exception -> { + handleAdException(jobParameter, lockService, lock, detectionStartTime, executionStartTime, exception, recorder, detector); + })); } catch (Exception e) { indexAnomalyResultException( jobParameter, @@ -672,11 +645,9 @@ private void releaseLock(Job jobParameter, LockService lockService, LockModel lo lockService .release( lock, - ActionListener - .wrap( - released -> { log.info("Released lock for AD job {}", jobParameter.getName()); }, - exception -> { log.error("Failed to release lock for AD job: " + jobParameter.getName(), exception); } - ) + ActionListener.wrap(released -> { log.info("Released lock for AD job {}", jobParameter.getName()); }, exception -> { + log.error("Failed to release lock for AD job: " + jobParameter.getName(), exception); + }) ); } } diff --git a/src/main/java/org/opensearch/ad/AnomalyDetectorRunner.java b/src/main/java/org/opensearch/ad/AnomalyDetectorRunner.java index fe0a0047e..c5336316c 100644 --- a/src/main/java/org/opensearch/ad/AnomalyDetectorRunner.java +++ b/src/main/java/org/opensearch/ad/AnomalyDetectorRunner.java @@ -84,11 +84,9 @@ public void executeDetector( listener.onResponse(Collections.emptyList()); return; } - ActionListener entityAnomalyResultListener = ActionListener - .wrap( - entityAnomalyResult -> { listener.onResponse(entityAnomalyResult.getAnomalyResults()); }, - e -> onFailure(e, listener, detector.getId()) - ); + ActionListener entityAnomalyResultListener = ActionListener.wrap(entityAnomalyResult -> { + listener.onResponse(entityAnomalyResult.getAnomalyResults()); + }, e -> onFailure(e, listener, detector.getId())); MultiResponsesDelegateActionListener multiEntitiesResponseListener = new MultiResponsesDelegateActionListener( entityAnomalyResultListener, diff --git a/src/main/java/org/opensearch/ad/caching/PriorityCache.java b/src/main/java/org/opensearch/ad/caching/PriorityCache.java index 701a7ba13..6315d4c0c 100644 --- a/src/main/java/org/opensearch/ad/caching/PriorityCache.java +++ b/src/main/java/org/opensearch/ad/caching/PriorityCache.java @@ -133,12 +133,9 @@ public PriorityCache( Duration inactiveEntityTtl = DateUtils.toDuration(checkpointTtl.get(settings)); this.inActiveEntities = createInactiveCache(inactiveEntityTtl, maxInactiveStates); - clusterService - .getClusterSettings() - .addSettingsUpdateConsumer( - checkpointTtl, - it -> { this.inActiveEntities = createInactiveCache(DateUtils.toDuration(it), maxInactiveStates); } - ); + clusterService.getClusterSettings().addSettingsUpdateConsumer(checkpointTtl, it -> { + this.inActiveEntities = createInactiveCache(DateUtils.toDuration(it), maxInactiveStates); + }); this.threadPool = threadPool; this.random = new Random(42); @@ -163,19 +160,15 @@ public ModelState get(String modelId, AnomalyDetector detector) { // during maintenance period, stop putting new entries if (!maintenanceLock.isLocked() && modelState == null) { if (ADEnabledSetting.isDoorKeeperInCacheEnabled()) { - DoorKeeper doorKeeper = doorKeepers - .computeIfAbsent( - detectorId, - id -> { - // reset every 60 intervals - return new DoorKeeper( - TimeSeriesSettings.DOOR_KEEPER_FOR_CACHE_MAX_INSERTION, - TimeSeriesSettings.DOOR_KEEPER_FALSE_POSITIVE_RATE, - detector.getIntervalDuration().multipliedBy(TimeSeriesSettings.DOOR_KEEPER_MAINTENANCE_FREQ), - clock - ); - } + DoorKeeper doorKeeper = doorKeepers.computeIfAbsent(detectorId, id -> { + // reset every 60 intervals + return new DoorKeeper( + TimeSeriesSettings.DOOR_KEEPER_FOR_CACHE_MAX_INSERTION, + TimeSeriesSettings.DOOR_KEEPER_FALSE_POSITIVE_RATE, + detector.getIntervalDuration().multipliedBy(TimeSeriesSettings.DOOR_KEEPER_MAINTENANCE_FREQ), + clock ); + }); // first hit, ignore // since door keeper may get reset during maintenance, it is possible diff --git a/src/main/java/org/opensearch/ad/cluster/ADClusterEventListener.java b/src/main/java/org/opensearch/ad/cluster/ADClusterEventListener.java index 91594cf0f..4f629c7bb 100644 --- a/src/main/java/org/opensearch/ad/cluster/ADClusterEventListener.java +++ b/src/main/java/org/opensearch/ad/cluster/ADClusterEventListener.java @@ -72,19 +72,9 @@ public void clusterChanged(ClusterChangedEvent event) { if (delta.removed() || delta.added()) { LOG.info(NODE_CHANGED_MSG + ", node removed: {}, node added: {}", delta.removed(), delta.added()); hashRing.addNodeChangeEvent(); - hashRing - .buildCircles( - delta, - ActionListener - .runAfter( - ActionListener - .wrap( - hasRingBuildDone -> { LOG.info("Hash ring build result: {}", hasRingBuildDone); }, - e -> { LOG.error("Failed updating AD version hash ring", e); } - ), - () -> inProgress.release() - ) - ); + hashRing.buildCircles(delta, ActionListener.runAfter(ActionListener.wrap(hasRingBuildDone -> { + LOG.info("Hash ring build result: {}", hasRingBuildDone); + }, e -> { LOG.error("Failed updating AD version hash ring", e); }), () -> inProgress.release())); } else { inProgress.release(); } diff --git a/src/main/java/org/opensearch/ad/cluster/DailyCron.java b/src/main/java/org/opensearch/ad/cluster/DailyCron.java index d0366350d..2692608d2 100644 --- a/src/main/java/org/opensearch/ad/cluster/DailyCron.java +++ b/src/main/java/org/opensearch/ad/cluster/DailyCron.java @@ -58,26 +58,17 @@ public void run() { ) ) .setIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN); - clientUtil - .execute( - DeleteByQueryAction.INSTANCE, - deleteRequest, - ActionListener - .wrap( - response -> { - // if 0 docs get deleted, it means our query cannot find any matching doc - LOG.info("{} " + CHECKPOINT_DELETED_MSG, response.getDeleted()); - }, - exception -> { - if (exception instanceof IndexNotFoundException) { - LOG.info(CHECKPOINT_NOT_EXIST_MSG); - } else { - // Gonna eventually delete in maintenance window. - LOG.error(CANNOT_DELETE_OLD_CHECKPOINT_MSG, exception); - } - } - ) - ); + clientUtil.execute(DeleteByQueryAction.INSTANCE, deleteRequest, ActionListener.wrap(response -> { + // if 0 docs get deleted, it means our query cannot find any matching doc + LOG.info("{} " + CHECKPOINT_DELETED_MSG, response.getDeleted()); + }, exception -> { + if (exception instanceof IndexNotFoundException) { + LOG.info(CHECKPOINT_NOT_EXIST_MSG); + } else { + // Gonna eventually delete in maintenance window. + LOG.error(CANNOT_DELETE_OLD_CHECKPOINT_MSG, exception); + } + })); } } diff --git a/src/main/java/org/opensearch/ad/cluster/HashRing.java b/src/main/java/org/opensearch/ad/cluster/HashRing.java index ee901efae..30ea1724f 100644 --- a/src/main/java/org/opensearch/ad/cluster/HashRing.java +++ b/src/main/java/org/opensearch/ad/cluster/HashRing.java @@ -177,13 +177,9 @@ public void buildCirclesForRealtimeAD() { if (nodeChangeEvents.isEmpty()) { return; } - buildCircles( - ActionListener - .wrap( - r -> { LOG.debug("build circles on AD versions successfully"); }, - e -> { LOG.error("Failed to build circles on AD versions", e); } - ) - ); + buildCircles(ActionListener.wrap(r -> { LOG.debug("build circles on AD versions successfully"); }, e -> { + LOG.error("Failed to build circles on AD versions", e); + })); } /** diff --git a/src/main/java/org/opensearch/ad/cluster/diskcleanup/ModelCheckpointIndexRetention.java b/src/main/java/org/opensearch/ad/cluster/diskcleanup/ModelCheckpointIndexRetention.java index 03ed36e76..28fc05e37 100644 --- a/src/main/java/org/opensearch/ad/cluster/diskcleanup/ModelCheckpointIndexRetention.java +++ b/src/main/java/org/opensearch/ad/cluster/diskcleanup/ModelCheckpointIndexRetention.java @@ -66,12 +66,12 @@ public void run() { .lte(clock.millis() - defaultCheckpointTtl.toMillis()) .format(ADCommonName.EPOCH_MILLIS_FORMAT) ), - ActionListener - .wrap( - response -> { cleanupBasedOnShardSize(defaultCheckpointTtl.minusDays(1)); }, - // The docs will be deleted in next scheduled windows. No need for retrying. - exception -> LOG.error("delete docs by query fails for checkpoint index", exception) - ) + ActionListener.wrap(response -> { + cleanupBasedOnShardSize(defaultCheckpointTtl.minusDays(1)); + }, + // The docs will be deleted in next scheduled windows. No need for retrying. + exception -> LOG.error("delete docs by query fails for checkpoint index", exception) + ) ); } diff --git a/src/main/java/org/opensearch/ad/indices/ADIndexManagement.java b/src/main/java/org/opensearch/ad/indices/ADIndexManagement.java index 7653d5dcf..95ce25faa 100644 --- a/src/main/java/org/opensearch/ad/indices/ADIndexManagement.java +++ b/src/main/java/org/opensearch/ad/indices/ADIndexManagement.java @@ -101,9 +101,9 @@ public ADIndexManagement( historyRolloverPeriod = it; rescheduleRollover(); }); - this.clusterService - .getClusterSettings() - .addSettingsUpdateConsumer(AD_RESULT_HISTORY_RETENTION_PERIOD, it -> { historyRetentionPeriod = it; }); + this.clusterService.getClusterSettings().addSettingsUpdateConsumer(AD_RESULT_HISTORY_RETENTION_PERIOD, it -> { + historyRetentionPeriod = it; + }); this.clusterService.getClusterSettings().addSettingsUpdateConsumer(AD_MAX_PRIMARY_SHARDS, it -> maxPrimaryShards = it); } diff --git a/src/main/java/org/opensearch/ad/ml/CheckpointDao.java b/src/main/java/org/opensearch/ad/ml/CheckpointDao.java index b1d7db46e..adb097cb6 100644 --- a/src/main/java/org/opensearch/ad/ml/CheckpointDao.java +++ b/src/main/java/org/opensearch/ad/ml/CheckpointDao.java @@ -603,14 +603,9 @@ private void deserializeTRCFModel( } String thresholdingModelId = SingleStreamModelIdMapper.getThresholdModelIdFromRCFModelId(rcfModelId); // query for threshold model and combinne rcf and threshold model into a ThresholdedRandomCutForest - getThresholdModel( - thresholdingModelId, - ActionListener - .wrap( - thresholdingModel -> { listener.onResponse(convertToTRCF(forest, thresholdingModel)); }, - listener::onFailure - ) - ); + getThresholdModel(thresholdingModelId, ActionListener.wrap(thresholdingModel -> { + listener.onResponse(convertToTRCF(forest, thresholdingModel)); + }, listener::onFailure)); } } catch (Exception e) { logger.error(new ParameterizedMessage("Unexpected error when deserializing [{}]", rcfModelId), e); @@ -627,12 +622,9 @@ private void deserializeTRCFModel( * @param listener Listener to return a pair of entity model and its last checkpoint time */ public void deserializeModelCheckpoint(String modelId, ActionListener>> listener) { - clientUtil - .asyncRequest( - new GetRequest(indexName, modelId), - client::get, - ActionListener.wrap(response -> { listener.onResponse(processGetResponse(response, modelId)); }, listener::onFailure) - ); + clientUtil.asyncRequest(new GetRequest(indexName, modelId), client::get, ActionListener.wrap(response -> { + listener.onResponse(processGetResponse(response, modelId)); + }, listener::onFailure)); } /** @@ -661,18 +653,14 @@ public void getTRCFModel(String modelId, ActionListenerasyncRequest( new GetRequest(indexName, modelId), client::get, - ActionListener - .wrap( - response -> deserializeTRCFModel(response, modelId, listener), - exception -> { - // expected exception, don't print stack trace - if (exception instanceof IndexNotFoundException) { - listener.onResponse(Optional.empty()); - } else { - listener.onFailure(exception); - } - } - ) + ActionListener.wrap(response -> deserializeTRCFModel(response, modelId, listener), exception -> { + // expected exception, don't print stack trace + if (exception instanceof IndexNotFoundException) { + listener.onResponse(Optional.empty()); + } else { + listener.onFailure(exception); + } + }) ); } @@ -697,16 +685,14 @@ public void getThresholdModel(String modelId, ActionListener { - // expected exception, don't print stack trace - if (exception instanceof IndexNotFoundException) { - listener.onResponse(Optional.empty()); - } else { - listener.onFailure(exception); - } + }, exception -> { + // expected exception, don't print stack trace + if (exception instanceof IndexNotFoundException) { + listener.onResponse(Optional.empty()); + } else { + listener.onFailure(exception); } - )); + })); } private Optional processThresholdModelCheckpoint(GetResponse response) { diff --git a/src/main/java/org/opensearch/ad/ml/EntityColdStarter.java b/src/main/java/org/opensearch/ad/ml/EntityColdStarter.java index 29de17d02..1044b84ce 100644 --- a/src/main/java/org/opensearch/ad/ml/EntityColdStarter.java +++ b/src/main/java/org/opensearch/ad/ml/EntityColdStarter.java @@ -248,19 +248,15 @@ private void coldStart( boolean earlyExit = true; try { - DoorKeeper doorKeeper = doorKeepers - .computeIfAbsent( - detectorId, - id -> { - // reset every 60 intervals - return new DoorKeeper( - TimeSeriesSettings.DOOR_KEEPER_FOR_COLD_STARTER_MAX_INSERTION, - TimeSeriesSettings.DOOR_KEEPER_FALSE_POSITIVE_RATE, - detector.getIntervalDuration().multipliedBy(TimeSeriesSettings.DOOR_KEEPER_MAINTENANCE_FREQ), - clock - ); - } + DoorKeeper doorKeeper = doorKeepers.computeIfAbsent(detectorId, id -> { + // reset every 60 intervals + return new DoorKeeper( + TimeSeriesSettings.DOOR_KEEPER_FOR_COLD_STARTER_MAX_INSERTION, + TimeSeriesSettings.DOOR_KEEPER_FALSE_POSITIVE_RATE, + detector.getIntervalDuration().multipliedBy(TimeSeriesSettings.DOOR_KEEPER_MAINTENANCE_FREQ), + clock ); + }); // Won't retry cold start within 60 intervals for an entity if (doorKeeper.mightContain(modelId)) { diff --git a/src/main/java/org/opensearch/ad/ml/ModelManager.java b/src/main/java/org/opensearch/ad/ml/ModelManager.java index f77eecb16..b95cf7f5f 100644 --- a/src/main/java/org/opensearch/ad/ml/ModelManager.java +++ b/src/main/java/org/opensearch/ad/ml/ModelManager.java @@ -662,12 +662,16 @@ public Map getModelSize(String detectorId) { .entrySet() .stream() .filter(entry -> SingleStreamModelIdMapper.getDetectorIdForModelId(entry.getKey()).equals(detectorId)) - .forEach(entry -> { res.put(entry.getKey(), memoryTracker.estimateTRCFModelSize(entry.getValue().getModel())); }); + .forEach(entry -> { + res.put(entry.getKey(), memoryTracker.estimateTRCFModelSize(entry.getValue().getModel())); + }); thresholds .entrySet() .stream() .filter(entry -> SingleStreamModelIdMapper.getDetectorIdForModelId(entry.getKey()).equals(detectorId)) - .forEach(entry -> { res.put(entry.getKey(), (long) memoryTracker.getThresholdModelBytes()); }); + .forEach(entry -> { + res.put(entry.getKey(), (long) memoryTracker.getThresholdModelBytes()); + }); return res; } diff --git a/src/main/java/org/opensearch/ad/rest/handler/AbstractAnomalyDetectorActionHandler.java b/src/main/java/org/opensearch/ad/rest/handler/AbstractAnomalyDetectorActionHandler.java index 102b3e3fd..614d47bee 100644 --- a/src/main/java/org/opensearch/ad/rest/handler/AbstractAnomalyDetectorActionHandler.java +++ b/src/main/java/org/opensearch/ad/rest/handler/AbstractAnomalyDetectorActionHandler.java @@ -904,20 +904,14 @@ protected void validateAnomalyDetectorFeatures(String detectorId, boolean indexi return; } // checking runtime error from feature query - ActionListener>> validateFeatureQueriesListener = ActionListener - .wrap( - response -> { checkADNameExists(detectorId, indexingDryRun); }, - exception -> { - listener - .onFailure( - new ValidationException( - exception.getMessage(), - ValidationIssueType.FEATURE_ATTRIBUTES, - ValidationAspect.DETECTOR - ) - ); - } - ); + ActionListener>> validateFeatureQueriesListener = ActionListener.wrap(response -> { + checkADNameExists(detectorId, indexingDryRun); + }, exception -> { + listener + .onFailure( + new ValidationException(exception.getMessage(), ValidationIssueType.FEATURE_ATTRIBUTES, ValidationAspect.DETECTOR) + ); + }); MultiResponsesDelegateActionListener>> multiFeatureQueriesResponseListener = new MultiResponsesDelegateActionListener>>( validateFeatureQueriesListener, diff --git a/src/main/java/org/opensearch/ad/rest/handler/IndexAnomalyDetectorJobActionHandler.java b/src/main/java/org/opensearch/ad/rest/handler/IndexAnomalyDetectorJobActionHandler.java index 9b234d3b4..fd59759fc 100644 --- a/src/main/java/org/opensearch/ad/rest/handler/IndexAnomalyDetectorJobActionHandler.java +++ b/src/main/java/org/opensearch/ad/rest/handler/IndexAnomalyDetectorJobActionHandler.java @@ -252,21 +252,12 @@ private void onGetAnomalyDetectorJobForWrite( ); // Get latest realtime task and check its state before index job. Will reset running realtime task // as STOPPED first if job disabled, then start new job and create new realtime task. - adTaskManager - .startDetector( - detector, - null, - job.getUser(), - transportService, - ActionListener - .wrap( - r -> { indexAnomalyDetectorJob(newJob, null, listener); }, - e -> { - // Have logged error message in ADTaskManager#startDetector - listener.onFailure(e); - } - ) - ); + adTaskManager.startDetector(detector, null, job.getUser(), transportService, ActionListener.wrap(r -> { + indexAnomalyDetectorJob(newJob, null, listener); + }, e -> { + // Have logged error message in ADTaskManager#startDetector + listener.onFailure(e); + })); } } catch (IOException e) { String message = "Failed to parse anomaly detector job " + job.getName(); @@ -274,14 +265,9 @@ private void onGetAnomalyDetectorJobForWrite( listener.onFailure(new OpenSearchStatusException(message, RestStatus.INTERNAL_SERVER_ERROR)); } } else { - adTaskManager - .startDetector( - detector, - null, - job.getUser(), - transportService, - ActionListener.wrap(r -> { indexAnomalyDetectorJob(job, null, listener); }, e -> listener.onFailure(e)) - ); + adTaskManager.startDetector(detector, null, job.getUser(), transportService, ActionListener.wrap(r -> { + indexAnomalyDetectorJob(job, null, listener); + }, e -> listener.onFailure(e))); } } diff --git a/src/main/java/org/opensearch/ad/rest/handler/ModelValidationActionHandler.java b/src/main/java/org/opensearch/ad/rest/handler/ModelValidationActionHandler.java index 4b152d93e..1f26d6bbd 100644 --- a/src/main/java/org/opensearch/ad/rest/handler/ModelValidationActionHandler.java +++ b/src/main/java/org/opensearch/ad/rest/handler/ModelValidationActionHandler.java @@ -731,16 +731,12 @@ private void processTopEntityResults(SearchResponse response, long latestTime) { } private void checkFeatureQueryDelegate(long latestTime) throws IOException { - ActionListener> validateFeatureQueriesListener = ActionListener - .wrap( - response -> { windowDelayRecommendation(latestTime); }, - exception -> { - listener - .onFailure( - new ValidationException(exception.getMessage(), ValidationIssueType.FEATURE_ATTRIBUTES, ValidationAspect.MODEL) - ); - } - ); + ActionListener> validateFeatureQueriesListener = ActionListener.wrap(response -> { + windowDelayRecommendation(latestTime); + }, exception -> { + listener + .onFailure(new ValidationException(exception.getMessage(), ValidationIssueType.FEATURE_ATTRIBUTES, ValidationAspect.MODEL)); + }); MultiResponsesDelegateActionListener> multiFeatureQueriesResponseListener = new MultiResponsesDelegateActionListener<>( validateFeatureQueriesListener, diff --git a/src/main/java/org/opensearch/ad/task/ADBatchTaskRunner.java b/src/main/java/org/opensearch/ad/task/ADBatchTaskRunner.java index 391d87e26..4ed6e862d 100644 --- a/src/main/java/org/opensearch/ad/task/ADBatchTaskRunner.java +++ b/src/main/java/org/opensearch/ad/task/ADBatchTaskRunner.java @@ -606,11 +606,9 @@ public void forwardOrExecuteADTask( .updateADTask( adTask.getTaskId(), updatedFields, - ActionListener - .wrap( - r -> forwardOrExecuteEntityTask(adTask, transportService, workerNodeResponseListener), - e -> { workerNodeResponseListener.onFailure(e); } - ) + ActionListener.wrap(r -> forwardOrExecuteEntityTask(adTask, transportService, workerNodeResponseListener), e -> { + workerNodeResponseListener.onFailure(e); + }) ); } } catch (Exception e) { diff --git a/src/main/java/org/opensearch/ad/task/ADTaskManager.java b/src/main/java/org/opensearch/ad/task/ADTaskManager.java index 8b67e746a..c89c605fd 100644 --- a/src/main/java/org/opensearch/ad/task/ADTaskManager.java +++ b/src/main/java/org/opensearch/ad/task/ADTaskManager.java @@ -253,18 +253,9 @@ public ADTaskManager( .withType(TransportRequestOptions.Type.REG) .withTimeout(AD_REQUEST_TIMEOUT.get(settings)) .build(); - clusterService - .getClusterSettings() - .addSettingsUpdateConsumer( - AD_REQUEST_TIMEOUT, - it -> { - transportRequestOptions = TransportRequestOptions - .builder() - .withType(TransportRequestOptions.Type.REG) - .withTimeout(it) - .build(); - } - ); + clusterService.getClusterSettings().addSettingsUpdateConsumer(AD_REQUEST_TIMEOUT, it -> { + transportRequestOptions = TransportRequestOptions.builder().withType(TransportRequestOptions.Type.REG).withTimeout(it).build(); + }); this.threadPool = threadPool; this.checkingTaskSlot = new Semaphore(1); this.scaleEntityTaskLane = new Semaphore(1); @@ -1241,16 +1232,12 @@ private void stopHistoricalAnalysis( String userName = user == null ? null : user.getName(); ADCancelTaskRequest cancelTaskRequest = new ADCancelTaskRequest(detectorId, taskId, userName, dataNodes); - client - .execute( - ADCancelTaskAction.INSTANCE, - cancelTaskRequest, - ActionListener - .wrap(response -> { listener.onResponse(new AnomalyDetectorJobResponse(taskId, 0, 0, 0, RestStatus.OK)); }, e -> { - logger.error("Failed to cancel AD task " + taskId + ", detector id: " + detectorId, e); - listener.onFailure(e); - }) - ); + client.execute(ADCancelTaskAction.INSTANCE, cancelTaskRequest, ActionListener.wrap(response -> { + listener.onResponse(new AnomalyDetectorJobResponse(taskId, 0, 0, 0, RestStatus.OK)); + }, e -> { + logger.error("Failed to cancel AD task " + taskId + ", detector id: " + detectorId, e); + listener.onFailure(e); + })); } private boolean lastUpdateTimeOfHistoricalTaskExpired(ADTask adTask) { @@ -1359,16 +1346,9 @@ public void cleanDetectorCache( protected void cleanDetectorCache(ADTask adTask, TransportService transportService, ExecutorFunction function) { String detectorId = adTask.getConfigId(); String taskId = adTask.getTaskId(); - cleanDetectorCache( - adTask, - transportService, - function, - ActionListener - .wrap( - r -> { logger.debug("Successfully cleaned cache for detector {}, task {}", detectorId, taskId); }, - e -> { logger.error("Failed to clean cache for detector " + detectorId + ", task " + taskId, e); } - ) - ); + cleanDetectorCache(adTask, transportService, function, ActionListener.wrap(r -> { + logger.debug("Successfully cleaned cache for detector {}, task {}", detectorId, taskId); + }, e -> { logger.error("Failed to clean cache for detector " + detectorId + ", task " + taskId, e); })); } /** @@ -1840,14 +1820,9 @@ public void updateADTask(String taskId, Map updatedFields, Actio * @param taskId AD task id */ public void deleteADTask(String taskId) { - deleteADTask( - taskId, - ActionListener - .wrap( - r -> { logger.info("Deleted AD task {} with status: {}", taskId, r.status()); }, - e -> { logger.error("Failed to delete AD task " + taskId, e); } - ) - ); + deleteADTask(taskId, ActionListener.wrap(r -> { logger.info("Deleted AD task {} with status: {}", taskId, r.status()); }, e -> { + logger.error("Failed to delete AD task " + taskId, e); + })); } /** @@ -1925,16 +1900,12 @@ private void deleteADResultOfDetector(String detectorId) { logger.info("Start to delete AD results of detector {}", detectorId); DeleteByQueryRequest deleteADResultsRequest = new DeleteByQueryRequest(ALL_AD_RESULTS_INDEX_PATTERN); deleteADResultsRequest.setQuery(new TermQueryBuilder(DETECTOR_ID_FIELD, detectorId)); - client - .execute( - DeleteByQueryAction.INSTANCE, - deleteADResultsRequest, - ActionListener - .wrap(response -> { logger.debug("Successfully deleted AD results of detector " + detectorId); }, exception -> { - logger.error("Failed to delete AD results of detector " + detectorId, exception); - adTaskCacheManager.addDeletedDetector(detectorId); - }) - ); + client.execute(DeleteByQueryAction.INSTANCE, deleteADResultsRequest, ActionListener.wrap(response -> { + logger.debug("Successfully deleted AD results of detector " + detectorId); + }, exception -> { + logger.error("Failed to delete AD results of detector " + detectorId, exception); + adTaskCacheManager.addDeletedDetector(detectorId); + })); } /** @@ -2497,15 +2468,9 @@ public void runNextEntityForHCADHistorical( ActionListener listener ) { String detectorId = adTask.getConfigId(); - int scaleDelta = scaleTaskSlots( - adTask, - transportService, - ActionListener - .wrap( - r -> { logger.debug("Scale up task slots done for detector {}, task {}", detectorId, adTask.getTaskId()); }, - e -> { logger.error("Failed to scale up task slots for task " + adTask.getTaskId(), e); } - ) - ); + int scaleDelta = scaleTaskSlots(adTask, transportService, ActionListener.wrap(r -> { + logger.debug("Scale up task slots done for detector {}, task {}", detectorId, adTask.getTaskId()); + }, e -> { logger.error("Failed to scale up task slots for task " + adTask.getTaskId(), e); })); if (scaleDelta < 0) { logger .warn( @@ -2732,16 +2697,12 @@ public ADTaskProfile getLocalADTaskProfilesByDetectorId(String detectorId) { detectorTaskProfile.setDetectorTaskSlots(1); } } - threadPool - .executor(AD_BATCH_TASK_THREAD_POOL_NAME) - .execute( - () -> { - // Clean expired HC batch task run states as it may exists after HC historical analysis done if user cancel - // before querying top entities done. We will clean it in hourly cron, check "maintainRunningHistoricalTasks" - // method. Clean it up here when get task profile to release memory earlier. - adTaskCacheManager.cleanExpiredHCBatchTaskRunStates(); - } - ); + threadPool.executor(AD_BATCH_TASK_THREAD_POOL_NAME).execute(() -> { + // Clean expired HC batch task run states as it may exists after HC historical analysis done if user cancel + // before querying top entities done. We will clean it in hourly cron, check "maintainRunningHistoricalTasks" + // method. Clean it up here when get task profile to release memory earlier. + adTaskCacheManager.cleanExpiredHCBatchTaskRunStates(); + }); logger.debug("Local AD task profile of detector {}: {}", detectorId, detectorTaskProfile); return detectorTaskProfile; } @@ -3048,21 +3009,9 @@ private void maintainRunningHistoricalTask(ConcurrentLinkedQueue taskQue resetHistoricalDetectorTaskState(ImmutableList.of(adTask), () -> { logger.debug("Finished maintaining running historical task {}", adTask.getTaskId()); maintainRunningHistoricalTask(taskQueue, transportService); - }, - transportService, - ActionListener - .wrap( - r -> { - logger - .debug( - "Reset historical task state done for task {}, detector {}", - adTask.getTaskId(), - adTask.getConfigId() - ); - }, - e -> { logger.error("Failed to reset historical task state for task " + adTask.getTaskId(), e); } - ) - ); + }, transportService, ActionListener.wrap(r -> { + logger.debug("Reset historical task state done for task {}, detector {}", adTask.getTaskId(), adTask.getConfigId()); + }, e -> { logger.error("Failed to reset historical task state for task " + adTask.getTaskId(), e); })); }, TimeValue.timeValueSeconds(DEFAULT_MAINTAIN_INTERVAL_IN_SECONDS), AD_BATCH_TASK_THREAD_POOL_NAME); } diff --git a/src/main/java/org/opensearch/ad/transport/DeleteAnomalyDetectorTransportAction.java b/src/main/java/org/opensearch/ad/transport/DeleteAnomalyDetectorTransportAction.java index 6ec539790..e37353b95 100644 --- a/src/main/java/org/opensearch/ad/transport/DeleteAnomalyDetectorTransportAction.java +++ b/src/main/java/org/opensearch/ad/transport/DeleteAnomalyDetectorTransportAction.java @@ -155,25 +155,17 @@ private void deleteAnomalyDetectorJobDoc(String detectorId, ActionListener listener) { LOG.info("Delete detector info {}", detectorId); DeleteRequest deleteRequest = new DeleteRequest(ADCommonName.DETECTION_STATE_INDEX, detectorId); - client - .delete( - deleteRequest, - ActionListener - .wrap( - response -> { - // whether deleted state doc or not, continue as state doc may not exist - deleteAnomalyDetectorDoc(detectorId, listener); - }, - exception -> { - if (exception instanceof IndexNotFoundException) { - deleteAnomalyDetectorDoc(detectorId, listener); - } else { - LOG.error("Failed to delete detector state", exception); - listener.onFailure(exception); - } - } - ) - ); + client.delete(deleteRequest, ActionListener.wrap(response -> { + // whether deleted state doc or not, continue as state doc may not exist + deleteAnomalyDetectorDoc(detectorId, listener); + }, exception -> { + if (exception instanceof IndexNotFoundException) { + deleteAnomalyDetectorDoc(detectorId, listener); + } else { + LOG.error("Failed to delete detector state", exception); + listener.onFailure(exception); + } + })); } private void deleteAnomalyDetectorDoc(String detectorId, ActionListener listener) { diff --git a/src/main/java/org/opensearch/ad/transport/GetAnomalyDetectorTransportAction.java b/src/main/java/org/opensearch/ad/transport/GetAnomalyDetectorTransportAction.java index dd7f3b2bd..8aa4aa7d5 100644 --- a/src/main/java/org/opensearch/ad/transport/GetAnomalyDetectorTransportAction.java +++ b/src/main/java/org/opensearch/ad/transport/GetAnomalyDetectorTransportAction.java @@ -171,37 +171,27 @@ protected void getExecute(GetAnomalyDetectorRequest request, ActionListener { - listener - .onResponse( - new GetAnomalyDetectorResponse( - 0, - null, - 0, - 0, - null, - null, - false, - null, - null, - false, - null, - null, - profile, - true - ) - ); - }, - e -> listener.onFailure(e) + profileRunner.profile(detectorID, entity, entityProfilesToCollect, ActionListener.wrap(profile -> { + listener + .onResponse( + new GetAnomalyDetectorResponse( + 0, + null, + 0, + 0, + null, + null, + false, + null, + null, + false, + null, + null, + profile, + true ) - ); + ); + }, e -> listener.onFailure(e))); } else { Set profilesToCollect = getProfilesToCollect(typesStr, all); AnomalyDetectorProfileRunner profileRunner = new AnomalyDetectorProfileRunner( diff --git a/src/main/java/org/opensearch/ad/transport/SearchAnomalyResultTransportAction.java b/src/main/java/org/opensearch/ad/transport/SearchAnomalyResultTransportAction.java index 55fd33865..2ae1f93d5 100644 --- a/src/main/java/org/opensearch/ad/transport/SearchAnomalyResultTransportAction.java +++ b/src/main/java/org/opensearch/ad/transport/SearchAnomalyResultTransportAction.java @@ -185,20 +185,12 @@ void multiSearch( context.restore(); // Send multiple search to check which index a user has permission to read. If search all indices directly, // search request will throw exception if user has no permission to search any index. - client - .multiSearch( - multiSearchRequest, - ActionListener - .wrap( - multiSearchResponse -> { - processMultiSearchResponse(multiSearchResponse, targetIndices, readableIndices, request, listener); - }, - multiSearchException -> { - logger.error("Failed to search custom AD result indices", multiSearchException); - listener.onFailure(multiSearchException); - } - ) - ); + client.multiSearch(multiSearchRequest, ActionListener.wrap(multiSearchResponse -> { + processMultiSearchResponse(multiSearchResponse, targetIndices, readableIndices, request, listener); + }, multiSearchException -> { + logger.error("Failed to search custom AD result indices", multiSearchException); + listener.onFailure(multiSearchException); + })); } @VisibleForTesting diff --git a/src/main/java/org/opensearch/ad/transport/handler/AnomalyIndexHandler.java b/src/main/java/org/opensearch/ad/transport/handler/AnomalyIndexHandler.java index a9552671d..9d539f797 100644 --- a/src/main/java/org/opensearch/ad/transport/handler/AnomalyIndexHandler.java +++ b/src/main/java/org/opensearch/ad/transport/handler/AnomalyIndexHandler.java @@ -193,41 +193,34 @@ protected void save(T toSave, String detectorId, String indexName) { } void saveIteration(IndexRequest indexRequest, String detectorId, Iterator backoff) { - clientUtil - .asyncRequest( - indexRequest, - client::index, - ActionListener - .wrap( - response -> { LOG.debug(String.format(Locale.ROOT, SUCCESS_SAVING_MSG, detectorId)); }, - exception -> { - // OpenSearch has a thread pool and a queue for write per node. A thread - // pool will have N number of workers ready to handle the requests. When a - // request comes and if a worker is free , this is handled by the worker. Now by - // default the number of workers is equal to the number of cores on that CPU. - // When the workers are full and there are more write requests, the request - // will go to queue. The size of queue is also limited. If by default size is, - // say, 200 and if there happens more parallel requests than this, then those - // requests would be rejected as you can see OpenSearchRejectedExecutionException. - // So OpenSearchRejectedExecutionException is the way that OpenSearch tells us that - // it cannot keep up with the current indexing rate. - // When it happens, we should pause indexing a bit before trying again, ideally - // with randomized exponential backoff. - Throwable cause = ExceptionsHelper.unwrapCause(exception); - if (!(cause instanceof OpenSearchRejectedExecutionException) || !backoff.hasNext()) { - LOG.error(String.format(Locale.ROOT, FAIL_TO_SAVE_ERR_MSG, detectorId), cause); - } else { - TimeValue nextDelay = backoff.next(); - LOG.warn(String.format(Locale.ROOT, RETRY_SAVING_ERR_MSG, detectorId), cause); - threadPool - .schedule( - () -> saveIteration(BulkUtil.cloneIndexRequest(indexRequest), detectorId, backoff), - nextDelay, - ThreadPool.Names.SAME - ); - } - } - ) - ); + clientUtil.asyncRequest(indexRequest, client::index, ActionListener.wrap(response -> { + LOG.debug(String.format(Locale.ROOT, SUCCESS_SAVING_MSG, detectorId)); + }, exception -> { + // OpenSearch has a thread pool and a queue for write per node. A thread + // pool will have N number of workers ready to handle the requests. When a + // request comes and if a worker is free , this is handled by the worker. Now by + // default the number of workers is equal to the number of cores on that CPU. + // When the workers are full and there are more write requests, the request + // will go to queue. The size of queue is also limited. If by default size is, + // say, 200 and if there happens more parallel requests than this, then those + // requests would be rejected as you can see OpenSearchRejectedExecutionException. + // So OpenSearchRejectedExecutionException is the way that OpenSearch tells us that + // it cannot keep up with the current indexing rate. + // When it happens, we should pause indexing a bit before trying again, ideally + // with randomized exponential backoff. + Throwable cause = ExceptionsHelper.unwrapCause(exception); + if (!(cause instanceof OpenSearchRejectedExecutionException) || !backoff.hasNext()) { + LOG.error(String.format(Locale.ROOT, FAIL_TO_SAVE_ERR_MSG, detectorId), cause); + } else { + TimeValue nextDelay = backoff.next(); + LOG.warn(String.format(Locale.ROOT, RETRY_SAVING_ERR_MSG, detectorId), cause); + threadPool + .schedule( + () -> saveIteration(BulkUtil.cloneIndexRequest(indexRequest), detectorId, backoff), + nextDelay, + ThreadPool.Names.SAME + ); + } + })); } } diff --git a/src/main/java/org/opensearch/forecast/indices/ForecastIndexManagement.java b/src/main/java/org/opensearch/forecast/indices/ForecastIndexManagement.java index 234f4c8c8..f27aa749e 100644 --- a/src/main/java/org/opensearch/forecast/indices/ForecastIndexManagement.java +++ b/src/main/java/org/opensearch/forecast/indices/ForecastIndexManagement.java @@ -98,9 +98,9 @@ public ForecastIndexManagement( historyRolloverPeriod = it; rescheduleRollover(); }); - this.clusterService - .getClusterSettings() - .addSettingsUpdateConsumer(FORECAST_RESULT_HISTORY_RETENTION_PERIOD, it -> { historyRetentionPeriod = it; }); + this.clusterService.getClusterSettings().addSettingsUpdateConsumer(FORECAST_RESULT_HISTORY_RETENTION_PERIOD, it -> { + historyRetentionPeriod = it; + }); this.clusterService.getClusterSettings().addSettingsUpdateConsumer(FORECAST_MAX_PRIMARY_SHARDS, it -> maxPrimaryShards = it); diff --git a/src/main/java/org/opensearch/timeseries/feature/SearchFeatureDao.java b/src/main/java/org/opensearch/timeseries/feature/SearchFeatureDao.java index f69013670..ecb86f441 100644 --- a/src/main/java/org/opensearch/timeseries/feature/SearchFeatureDao.java +++ b/src/main/java/org/opensearch/timeseries/feature/SearchFeatureDao.java @@ -495,8 +495,9 @@ public void getMinDataTime(Config config, Optional entity, AnalysisType .trackTotalHits(false) .size(0); SearchRequest searchRequest = new SearchRequest().indices(config.getIndices().toArray(new String[0])).source(searchSourceBuilder); - final ActionListener searchResponseListener = ActionListener - .wrap(response -> { listener.onResponse(parseMinDataTime(response)); }, listener::onFailure); + final ActionListener searchResponseListener = ActionListener.wrap(response -> { + listener.onResponse(parseMinDataTime(response)); + }, listener::onFailure); // inject user role while searching. clientUtil .asyncRequestWithInjectedSecurity( @@ -554,11 +555,9 @@ public void getFeaturesForPeriodByBatch( logger.debug("Batch query for detector {}: {} ", detector.getId(), searchSourceBuilder); SearchRequest searchRequest = new SearchRequest(detector.getIndices().toArray(new String[0])).source(searchSourceBuilder); - final ActionListener searchResponseListener = ActionListener - .wrap( - response -> { listener.onResponse(parseBucketAggregationResponse(response, detector.getEnabledFeatureIds())); }, - listener::onFailure - ); + final ActionListener searchResponseListener = ActionListener.wrap(response -> { + listener.onResponse(parseBucketAggregationResponse(response, detector.getEnabledFeatureIds())); + }, listener::onFailure); // inject user role while searching. clientUtil .asyncRequestWithInjectedSecurity( diff --git a/src/main/java/org/opensearch/timeseries/util/ClientUtil.java b/src/main/java/org/opensearch/timeseries/util/ClientUtil.java index b04a8fbac..394065b2c 100644 --- a/src/main/java/org/opensearch/timeseries/util/ClientUtil.java +++ b/src/main/java/org/opensearch/timeseries/util/ClientUtil.java @@ -61,11 +61,8 @@ public void exe Request request, ActionListener listener ) { - client - .execute( - action, - request, - ActionListener.wrap(response -> { listener.onResponse(response); }, exception -> { listener.onFailure(exception); }) - ); + client.execute(action, request, ActionListener.wrap(response -> { listener.onResponse(response); }, exception -> { + listener.onFailure(exception); + })); } } diff --git a/src/main/java/org/opensearch/timeseries/util/ExceptionUtil.java b/src/main/java/org/opensearch/timeseries/util/ExceptionUtil.java index 82ed31b84..e4b7c751e 100644 --- a/src/main/java/org/opensearch/timeseries/util/ExceptionUtil.java +++ b/src/main/java/org/opensearch/timeseries/util/ExceptionUtil.java @@ -150,11 +150,9 @@ public static boolean isRetryAble(RestStatus status) { * @return the wrapped listener */ public static ActionListener wrapListener(ActionListener original, Exception exceptionToReturn, String detectorId) { - return ActionListener - .wrap( - r -> { original.onFailure(exceptionToReturn); }, - e -> { original.onFailure(selectHigherPriorityException(exceptionToReturn, e)); } - ); + return ActionListener.wrap(r -> { original.onFailure(exceptionToReturn); }, e -> { + original.onFailure(selectHigherPriorityException(exceptionToReturn, e)); + }); } /** diff --git a/src/test/java/org/opensearch/ad/cluster/HashRingTests.java b/src/test/java/org/opensearch/ad/cluster/HashRingTests.java index b18efd6bd..69bb38a57 100644 --- a/src/test/java/org/opensearch/ad/cluster/HashRingTests.java +++ b/src/test/java/org/opensearch/ad/cluster/HashRingTests.java @@ -208,7 +208,9 @@ public void testBuildAndGetOwningNodeWithSameLocalAdVersion() { .buildAndGetOwningNodeWithSameLocalAdVersion( "testModelId", node -> { assertTrue(node.isPresent()); }, - ActionListener.wrap(r -> {}, e -> { assertFalse("Failed to build hash ring", true); }) + ActionListener.wrap(r -> {}, e -> { + assertFalse("Failed to build hash ring", true); + }) ); } diff --git a/src/test/java/org/opensearch/ad/cluster/diskcleanup/IndexCleanupTests.java b/src/test/java/org/opensearch/ad/cluster/diskcleanup/IndexCleanupTests.java index 2830b5640..0748fe122 100644 --- a/src/test/java/org/opensearch/ad/cluster/diskcleanup/IndexCleanupTests.java +++ b/src/test/java/org/opensearch/ad/cluster/diskcleanup/IndexCleanupTests.java @@ -107,13 +107,9 @@ public void testDeleteDocsBasedOnShardSizeWithCleanupNeededAsTrue() throws Excep public void testDeleteDocsBasedOnShardSizeWithCleanupNeededAsFalse() throws Exception { long maxShardSize = 1000; when(storeStats.getSizeInBytes()).thenReturn(maxShardSize - 1); - indexCleanup - .deleteDocsBasedOnShardSize( - "indexname", - maxShardSize, - null, - ActionListener.wrap(Assert::assertFalse, exception -> { throw new RuntimeException(exception); }) - ); + indexCleanup.deleteDocsBasedOnShardSize("indexname", maxShardSize, null, ActionListener.wrap(Assert::assertFalse, exception -> { + throw new RuntimeException(exception); + })); } public void testDeleteDocsBasedOnShardSizeIndexNotExisted() throws Exception { diff --git a/src/test/java/org/opensearch/ad/feature/FeatureManagerTests.java b/src/test/java/org/opensearch/ad/feature/FeatureManagerTests.java index 95e667ff9..3c12cc99d 100644 --- a/src/test/java/org/opensearch/ad/feature/FeatureManagerTests.java +++ b/src/test/java/org/opensearch/ad/feature/FeatureManagerTests.java @@ -46,9 +46,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,6 +65,9 @@ import org.opensearch.timeseries.model.Entity; import org.opensearch.timeseries.model.IntervalTimeConfiguration; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @RunWith(JUnitParamsRunner.class) @SuppressWarnings("unchecked") public class FeatureManagerTests { diff --git a/src/test/java/org/opensearch/ad/feature/FeaturesTests.java b/src/test/java/org/opensearch/ad/feature/FeaturesTests.java index 447bdd6c4..7a6b3b8e1 100644 --- a/src/test/java/org/opensearch/ad/feature/FeaturesTests.java +++ b/src/test/java/org/opensearch/ad/feature/FeaturesTests.java @@ -18,12 +18,12 @@ import java.util.List; import java.util.Map.Entry; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.junit.Test; import org.junit.runner.RunWith; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @RunWith(JUnitParamsRunner.class) public class FeaturesTests { diff --git a/src/test/java/org/opensearch/ad/indices/AnomalyDetectionIndicesTests.java b/src/test/java/org/opensearch/ad/indices/AnomalyDetectionIndicesTests.java index 313800385..eb68bbe7f 100644 --- a/src/test/java/org/opensearch/ad/indices/AnomalyDetectionIndicesTests.java +++ b/src/test/java/org/opensearch/ad/indices/AnomalyDetectionIndicesTests.java @@ -77,24 +77,14 @@ public void testAnomalyDetectorIndexExists() throws IOException { } public void testAnomalyDetectorIndexExistsAndNotRecreate() throws IOException { - indices - .initConfigIndexIfAbsent( - TestHelpers - .createActionListener( - response -> response.isAcknowledged(), - failure -> { throw new RuntimeException("should not recreate index"); } - ) - ); + indices.initConfigIndexIfAbsent(TestHelpers.createActionListener(response -> response.isAcknowledged(), failure -> { + throw new RuntimeException("should not recreate index"); + })); TestHelpers.waitForIndexCreationToComplete(client(), CommonName.CONFIG_INDEX); if (client().admin().indices().prepareExists(CommonName.CONFIG_INDEX).get().isExists()) { - indices - .initConfigIndexIfAbsent( - TestHelpers - .createActionListener( - response -> { throw new RuntimeException("should not recreate index " + CommonName.CONFIG_INDEX); }, - failure -> { throw new RuntimeException("should not recreate index " + CommonName.CONFIG_INDEX); } - ) - ); + indices.initConfigIndexIfAbsent(TestHelpers.createActionListener(response -> { + throw new RuntimeException("should not recreate index " + CommonName.CONFIG_INDEX); + }, failure -> { throw new RuntimeException("should not recreate index " + CommonName.CONFIG_INDEX); })); } } @@ -114,26 +104,16 @@ public void testAnomalyResultIndexExists() throws IOException { public void testAnomalyResultIndexExistsAndNotRecreate() throws IOException { indices .initDefaultResultIndexIfAbsent( - TestHelpers - .createActionListener( - response -> logger.info("Acknowledged: " + response.isAcknowledged()), - failure -> { throw new RuntimeException("should not recreate index"); } - ) + TestHelpers.createActionListener(response -> logger.info("Acknowledged: " + response.isAcknowledged()), failure -> { + throw new RuntimeException("should not recreate index"); + }) ); TestHelpers.waitForIndexCreationToComplete(client(), ADCommonName.ANOMALY_RESULT_INDEX_ALIAS); if (client().admin().indices().prepareExists(ADCommonName.ANOMALY_RESULT_INDEX_ALIAS).get().isExists()) { - indices - .initDefaultResultIndexIfAbsent( - TestHelpers - .createActionListener( - response -> { - throw new RuntimeException("should not recreate index " + ADCommonName.ANOMALY_RESULT_INDEX_ALIAS); - }, - failure -> { - throw new RuntimeException("should not recreate index " + ADCommonName.ANOMALY_RESULT_INDEX_ALIAS, failure); - } - ) - ); + indices.initDefaultResultIndexIfAbsent(TestHelpers.createActionListener(response -> { + throw new RuntimeException("should not recreate index " + ADCommonName.ANOMALY_RESULT_INDEX_ALIAS); + }, failure -> { throw new RuntimeException("should not recreate index " + ADCommonName.ANOMALY_RESULT_INDEX_ALIAS, failure); }) + ); } } diff --git a/src/test/java/org/opensearch/ad/ml/CheckpointDaoTests.java b/src/test/java/org/opensearch/ad/ml/CheckpointDaoTests.java index 9a36be401..86c6ceb65 100644 --- a/src/test/java/org/opensearch/ad/ml/CheckpointDaoTests.java +++ b/src/test/java/org/opensearch/ad/ml/CheckpointDaoTests.java @@ -105,10 +105,6 @@ import org.opensearch.timeseries.constant.CommonName; import org.opensearch.timeseries.util.ClientUtil; -import test.org.opensearch.ad.util.JsonDeserializer; -import test.org.opensearch.ad.util.MLUtil; -import test.org.opensearch.ad.util.RandomModelStateConfig; - import com.amazon.randomcutforest.RandomCutForest; import com.amazon.randomcutforest.config.Precision; import com.amazon.randomcutforest.config.TransformMethod; @@ -124,6 +120,9 @@ import io.protostuff.LinkedBuffer; import io.protostuff.Schema; import io.protostuff.runtime.RuntimeSchema; +import test.org.opensearch.ad.util.JsonDeserializer; +import test.org.opensearch.ad.util.MLUtil; +import test.org.opensearch.ad.util.RandomModelStateConfig; public class CheckpointDaoTests extends OpenSearchTestCase { private static final Logger logger = LogManager.getLogger(CheckpointDaoTests.class); diff --git a/src/test/java/org/opensearch/ad/ml/EntityColdStarterTests.java b/src/test/java/org/opensearch/ad/ml/EntityColdStarterTests.java index 67ef7f7ab..2807c3207 100644 --- a/src/test/java/org/opensearch/ad/ml/EntityColdStarterTests.java +++ b/src/test/java/org/opensearch/ad/ml/EntityColdStarterTests.java @@ -62,16 +62,16 @@ import org.opensearch.timeseries.model.IntervalTimeConfiguration; import org.opensearch.timeseries.settings.TimeSeriesSettings; -import test.org.opensearch.ad.util.LabelledAnomalyGenerator; -import test.org.opensearch.ad.util.MLUtil; -import test.org.opensearch.ad.util.MultiDimDataWithTime; - import com.amazon.randomcutforest.config.Precision; import com.amazon.randomcutforest.config.TransformMethod; import com.amazon.randomcutforest.parkservices.AnomalyDescriptor; import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest; import com.google.common.collect.ImmutableList; +import test.org.opensearch.ad.util.LabelledAnomalyGenerator; +import test.org.opensearch.ad.util.MLUtil; +import test.org.opensearch.ad.util.MultiDimDataWithTime; + public class EntityColdStarterTests extends AbstractCosineDataTest { @BeforeClass diff --git a/src/test/java/org/opensearch/ad/ml/HCADModelPerfTests.java b/src/test/java/org/opensearch/ad/ml/HCADModelPerfTests.java index 46a2a3ec3..97424a25f 100644 --- a/src/test/java/org/opensearch/ad/ml/HCADModelPerfTests.java +++ b/src/test/java/org/opensearch/ad/ml/HCADModelPerfTests.java @@ -51,12 +51,12 @@ import org.opensearch.timeseries.model.IntervalTimeConfiguration; import org.opensearch.timeseries.settings.TimeSeriesSettings; -import test.org.opensearch.ad.util.LabelledAnomalyGenerator; -import test.org.opensearch.ad.util.MultiDimDataWithTime; - import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; import com.google.common.collect.ImmutableList; +import test.org.opensearch.ad.util.LabelledAnomalyGenerator; +import test.org.opensearch.ad.util.MultiDimDataWithTime; + @TimeoutSuite(millis = 60 * TimeUnits.MINUTE) // rcf may be slow due to bounding box cache disabled public class HCADModelPerfTests extends AbstractCosineDataTest { diff --git a/src/test/java/org/opensearch/ad/ml/HybridThresholdingModelTests.java b/src/test/java/org/opensearch/ad/ml/HybridThresholdingModelTests.java index be8ff9525..5e6ffdb9b 100644 --- a/src/test/java/org/opensearch/ad/ml/HybridThresholdingModelTests.java +++ b/src/test/java/org/opensearch/ad/ml/HybridThresholdingModelTests.java @@ -17,13 +17,13 @@ import java.util.Arrays; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.apache.commons.math3.distribution.NormalDistribution; import org.junit.Test; import org.junit.runner.RunWith; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @RunWith(JUnitParamsRunner.class) public class HybridThresholdingModelTests { diff --git a/src/test/java/org/opensearch/ad/ml/ModelManagerTests.java b/src/test/java/org/opensearch/ad/ml/ModelManagerTests.java index 837cb2d7f..5e09d0cd9 100644 --- a/src/test/java/org/opensearch/ad/ml/ModelManagerTests.java +++ b/src/test/java/org/opensearch/ad/ml/ModelManagerTests.java @@ -44,9 +44,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -79,14 +76,16 @@ import org.opensearch.timeseries.model.Entity; import org.opensearch.timeseries.util.DiscoveryNodeFilterer; -import test.org.opensearch.ad.util.MLUtil; -import test.org.opensearch.ad.util.RandomModelStateConfig; - import com.amazon.randomcutforest.RandomCutForest; import com.amazon.randomcutforest.parkservices.AnomalyDescriptor; import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest; import com.amazon.randomcutforest.returntypes.DiVector; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; +import test.org.opensearch.ad.util.MLUtil; +import test.org.opensearch.ad.util.RandomModelStateConfig; + @RunWith(JUnitParamsRunner.class) @SuppressWarnings("unchecked") public class ModelManagerTests { diff --git a/src/test/java/org/opensearch/ad/ml/ThresholdingResultTests.java b/src/test/java/org/opensearch/ad/ml/ThresholdingResultTests.java index 492bbec45..111041858 100644 --- a/src/test/java/org/opensearch/ad/ml/ThresholdingResultTests.java +++ b/src/test/java/org/opensearch/ad/ml/ThresholdingResultTests.java @@ -13,13 +13,13 @@ import static org.junit.Assert.assertEquals; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.junit.Test; import org.junit.runner.RunWith; import org.opensearch.ad.model.AnomalyResult; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @RunWith(JUnitParamsRunner.class) public class ThresholdingResultTests { diff --git a/src/test/java/org/opensearch/ad/mock/plugin/MockReindexPlugin.java b/src/test/java/org/opensearch/ad/mock/plugin/MockReindexPlugin.java index 07c116774..f8c54b112 100644 --- a/src/test/java/org/opensearch/ad/mock/plugin/MockReindexPlugin.java +++ b/src/test/java/org/opensearch/ad/mock/plugin/MockReindexPlugin.java @@ -122,11 +122,9 @@ protected void doExecute(Task task, DeleteByQueryRequest request, ActionListener .execute( BulkAction.INSTANCE, bulkRequest, - ActionListener - .wrap( - res -> { listener.onResponse(mockBulkByScrollResponse(totalHits)); }, - ex -> { listener.onFailure(ex); } - ) + ActionListener.wrap(res -> { listener.onResponse(mockBulkByScrollResponse(totalHits)); }, ex -> { + listener.onFailure(ex); + }) ); }, e -> { listener.onFailure(e); })); diff --git a/src/test/java/org/opensearch/ad/ratelimit/CheckpointReadWorkerTests.java b/src/test/java/org/opensearch/ad/ratelimit/CheckpointReadWorkerTests.java index 7d2a48e65..00580496d 100644 --- a/src/test/java/org/opensearch/ad/ratelimit/CheckpointReadWorkerTests.java +++ b/src/test/java/org/opensearch/ad/ratelimit/CheckpointReadWorkerTests.java @@ -78,11 +78,11 @@ import org.opensearch.timeseries.model.Entity; import org.opensearch.timeseries.stats.StatNames; +import com.fasterxml.jackson.core.JsonParseException; + import test.org.opensearch.ad.util.MLUtil; import test.org.opensearch.ad.util.RandomModelStateConfig; -import com.fasterxml.jackson.core.JsonParseException; - public class CheckpointReadWorkerTests extends AbstractRateLimitingTest { CheckpointReadWorker worker; diff --git a/src/test/java/org/opensearch/ad/rest/AnomalyDetectorRestApiIT.java b/src/test/java/org/opensearch/ad/rest/AnomalyDetectorRestApiIT.java index 691e6b439..3a02b1bf1 100644 --- a/src/test/java/org/opensearch/ad/rest/AnomalyDetectorRestApiIT.java +++ b/src/test/java/org/opensearch/ad/rest/AnomalyDetectorRestApiIT.java @@ -1505,75 +1505,62 @@ public void testSearchTopAnomalyResultsWithInvalidInputs() throws IOException { ); // Missing start time - Exception missingStartTimeException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId(), false, "{\"end_time_ms\":2}", client()); } - ); + Exception missingStartTimeException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), false, "{\"end_time_ms\":2}", client()); + }); assertTrue(missingStartTimeException.getMessage().contains("Must set both start time and end time with epoch of milliseconds")); // Missing end time - Exception missingEndTimeException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1}", client()); } - ); + Exception missingEndTimeException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1}", client()); + }); assertTrue(missingEndTimeException.getMessage().contains("Must set both start time and end time with epoch of milliseconds")); // Start time > end time - Exception invalidTimeException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":2, \"end_time_ms\":1}", client()); } - ); + Exception invalidTimeException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":2, \"end_time_ms\":1}", client()); + }); assertTrue(invalidTimeException.getMessage().contains("Start time should be before end time")); // Invalid detector ID - Exception invalidDetectorIdException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId() + "-invalid", false, "{\"start_time_ms\":1, \"end_time_ms\":2}", client()); } - ); + Exception invalidDetectorIdException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId() + "-invalid", false, "{\"start_time_ms\":1, \"end_time_ms\":2}", client()); + }); assertTrue(invalidDetectorIdException.getMessage().contains("Can't find config with id")); // Invalid order field - Exception invalidOrderException = expectThrows( - IOException.class, - () -> { - searchTopAnomalyResults( - detector.getId(), - false, - "{\"start_time_ms\":1, \"end_time_ms\":2, \"order\":\"invalid-order\"}", - client() - ); - } - ); + Exception invalidOrderException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults( + detector.getId(), + false, + "{\"start_time_ms\":1, \"end_time_ms\":2, \"order\":\"invalid-order\"}", + client() + ); + }); assertTrue(invalidOrderException.getMessage().contains("Ordering by invalid-order is not a valid option")); // Negative size field - Exception negativeSizeException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2, \"size\":-1}", client()); } - ); + Exception negativeSizeException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2, \"size\":-1}", client()); + }); assertTrue(negativeSizeException.getMessage().contains("Size must be a positive integer")); // Zero size field - Exception zeroSizeException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2, \"size\":0}", client()); } - ); + Exception zeroSizeException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2, \"size\":0}", client()); + }); assertTrue(zeroSizeException.getMessage().contains("Size must be a positive integer")); // Too large size field - Exception tooLargeSizeException = expectThrows( - IOException.class, - () -> { - searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2, \"size\":9999999}", client()); - } - ); + Exception tooLargeSizeException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2, \"size\":9999999}", client()); + }); assertTrue(tooLargeSizeException.getMessage().contains("Size cannot exceed")); // No existing task ID for detector - Exception noTaskIdException = expectThrows( - IOException.class, - () -> { searchTopAnomalyResults(detector.getId(), true, "{\"start_time_ms\":1, \"end_time_ms\":2}", client()); } - ); + Exception noTaskIdException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detector.getId(), true, "{\"start_time_ms\":1, \"end_time_ms\":2}", client()); + }); assertTrue(noTaskIdException.getMessage().contains("No historical tasks found for detector ID " + detector.getId())); // Invalid category fields @@ -1603,12 +1590,9 @@ public void testSearchTopAnomalyResultsWithInvalidInputs() throws IOException { true, client() ); - Exception noCategoryFieldsException = expectThrows( - IOException.class, - () -> { - searchTopAnomalyResults(detectorWithNoCategoryFields.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2}", client()); - } - ); + Exception noCategoryFieldsException = expectThrows(IOException.class, () -> { + searchTopAnomalyResults(detectorWithNoCategoryFields.getId(), false, "{\"start_time_ms\":1, \"end_time_ms\":2}", client()); + }); assertTrue( noCategoryFieldsException .getMessage() diff --git a/src/test/java/org/opensearch/ad/rest/SecureADRestIT.java b/src/test/java/org/opensearch/ad/rest/SecureADRestIT.java index 9dab61e84..dcadb41ac 100644 --- a/src/test/java/org/opensearch/ad/rest/SecureADRestIT.java +++ b/src/test/java/org/opensearch/ad/rest/SecureADRestIT.java @@ -333,10 +333,9 @@ public void testStartApiFilterByEnabled() throws IOException { // User Cat has AD full access, but is part of different backend role so Cat should not be able to access // Alice detector Instant now = Instant.now(); - Exception exception = expectThrows( - IOException.class, - () -> { startAnomalyDetector(aliceDetector.getId(), new DateRange(now.minus(10, ChronoUnit.DAYS), now), catClient); } - ); + Exception exception = expectThrows(IOException.class, () -> { + startAnomalyDetector(aliceDetector.getId(), new DateRange(now.minus(10, ChronoUnit.DAYS), now), catClient); + }); Assert.assertTrue(exception.getMessage().contains("User does not have permissions to access detector: " + aliceDetector.getId())); } diff --git a/src/test/java/org/opensearch/ad/stats/ADStatsTests.java b/src/test/java/org/opensearch/ad/stats/ADStatsTests.java index fb5bf220e..6cd95d36f 100644 --- a/src/test/java/org/opensearch/ad/stats/ADStatsTests.java +++ b/src/test/java/org/opensearch/ad/stats/ADStatsTests.java @@ -46,11 +46,11 @@ import org.opensearch.test.OpenSearchTestCase; import org.opensearch.timeseries.stats.StatNames; +import com.amazon.randomcutforest.RandomCutForest; + import test.org.opensearch.ad.util.MLUtil; import test.org.opensearch.ad.util.RandomModelStateConfig; -import com.amazon.randomcutforest.RandomCutForest; - public class ADStatsTests extends OpenSearchTestCase { private Map> statsMap; diff --git a/src/test/java/org/opensearch/ad/stats/suppliers/ModelsOnNodeSupplierTests.java b/src/test/java/org/opensearch/ad/stats/suppliers/ModelsOnNodeSupplierTests.java index c0173593c..fd56ae427 100644 --- a/src/test/java/org/opensearch/ad/stats/suppliers/ModelsOnNodeSupplierTests.java +++ b/src/test/java/org/opensearch/ad/stats/suppliers/ModelsOnNodeSupplierTests.java @@ -41,11 +41,11 @@ import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchTestCase; +import com.amazon.randomcutforest.RandomCutForest; + import test.org.opensearch.ad.util.MLUtil; import test.org.opensearch.ad.util.RandomModelStateConfig; -import com.amazon.randomcutforest.RandomCutForest; - public class ModelsOnNodeSupplierTests extends OpenSearchTestCase { private RandomCutForest rcf; private HybridThresholdingModel thresholdingModel; diff --git a/src/test/java/org/opensearch/ad/transport/ADStatsTests.java b/src/test/java/org/opensearch/ad/transport/ADStatsTests.java index e2edac7ea..4836825f3 100644 --- a/src/test/java/org/opensearch/ad/transport/ADStatsTests.java +++ b/src/test/java/org/opensearch/ad/transport/ADStatsTests.java @@ -48,11 +48,11 @@ import org.opensearch.timeseries.model.Entity; import org.opensearch.timeseries.stats.StatNames; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import test.org.opensearch.ad.util.JsonDeserializer; + public class ADStatsTests extends OpenSearchTestCase { String node1, nodeName1, clusterName; Map clusterStats; diff --git a/src/test/java/org/opensearch/ad/transport/AnomalyDetectorJobTransportActionTests.java b/src/test/java/org/opensearch/ad/transport/AnomalyDetectorJobTransportActionTests.java index 79002ab44..50765deb8 100644 --- a/src/test/java/org/opensearch/ad/transport/AnomalyDetectorJobTransportActionTests.java +++ b/src/test/java/org/opensearch/ad/transport/AnomalyDetectorJobTransportActionTests.java @@ -509,13 +509,9 @@ private long getExecutingADTask() { adStatsRequest.addAll(validStats); StatsAnomalyDetectorResponse statsResponse = client().execute(StatsAnomalyDetectorAction.INSTANCE, adStatsRequest).actionGet(5000); AtomicLong totalExecutingTask = new AtomicLong(0); - statsResponse - .getAdStatsResponse() - .getADStatsNodesResponse() - .getNodes() - .forEach( - node -> { totalExecutingTask.getAndAdd((Long) node.getStatsMap().get(StatNames.AD_EXECUTING_BATCH_TASK_COUNT.getName())); } - ); + statsResponse.getAdStatsResponse().getADStatsNodesResponse().getNodes().forEach(node -> { + totalExecutingTask.getAndAdd((Long) node.getStatsMap().get(StatNames.AD_EXECUTING_BATCH_TASK_COUNT.getName())); + }); return totalExecutingTask.get(); } } diff --git a/src/test/java/org/opensearch/ad/transport/AnomalyResultTests.java b/src/test/java/org/opensearch/ad/transport/AnomalyResultTests.java index 76b98abc8..46cfe22e8 100644 --- a/src/test/java/org/opensearch/ad/transport/AnomalyResultTests.java +++ b/src/test/java/org/opensearch/ad/transport/AnomalyResultTests.java @@ -129,10 +129,10 @@ import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.JsonElement; +import test.org.opensearch.ad.util.JsonDeserializer; + public class AnomalyResultTests extends AbstractTimeSeriesTest { private Settings settings; private TransportService transportService; diff --git a/src/test/java/org/opensearch/ad/transport/CronTransportActionTests.java b/src/test/java/org/opensearch/ad/transport/CronTransportActionTests.java index 06f375053..7c3de7ed2 100644 --- a/src/test/java/org/opensearch/ad/transport/CronTransportActionTests.java +++ b/src/test/java/org/opensearch/ad/transport/CronTransportActionTests.java @@ -43,10 +43,10 @@ import org.opensearch.timeseries.NodeStateManager; import org.opensearch.transport.TransportService; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.JsonElement; +import test.org.opensearch.ad.util.JsonDeserializer; + public class CronTransportActionTests extends AbstractTimeSeriesTest { private CronTransportAction action; private String localNodeID; diff --git a/src/test/java/org/opensearch/ad/transport/DeleteModelTransportActionTests.java b/src/test/java/org/opensearch/ad/transport/DeleteModelTransportActionTests.java index 62429f962..b76925492 100644 --- a/src/test/java/org/opensearch/ad/transport/DeleteModelTransportActionTests.java +++ b/src/test/java/org/opensearch/ad/transport/DeleteModelTransportActionTests.java @@ -48,10 +48,10 @@ import org.opensearch.timeseries.NodeStateManager; import org.opensearch.transport.TransportService; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.JsonElement; +import test.org.opensearch.ad.util.JsonDeserializer; + public class DeleteModelTransportActionTests extends AbstractTimeSeriesTest { private DeleteModelTransportAction action; private String localNodeID; diff --git a/src/test/java/org/opensearch/ad/transport/EntityResultTransportActionTests.java b/src/test/java/org/opensearch/ad/transport/EntityResultTransportActionTests.java index c48e35659..dd3432bca 100644 --- a/src/test/java/org/opensearch/ad/transport/EntityResultTransportActionTests.java +++ b/src/test/java/org/opensearch/ad/transport/EntityResultTransportActionTests.java @@ -91,13 +91,13 @@ import org.opensearch.timeseries.stats.StatNames; import org.opensearch.transport.TransportService; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + import test.org.opensearch.ad.util.JsonDeserializer; import test.org.opensearch.ad.util.MLUtil; import test.org.opensearch.ad.util.RandomModelStateConfig; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; - public class EntityResultTransportActionTests extends AbstractTimeSeriesTest { EntityResultTransportAction entityResult; ActionFilters actionFilters; diff --git a/src/test/java/org/opensearch/ad/transport/MultiEntityResultTests.java b/src/test/java/org/opensearch/ad/transport/MultiEntityResultTests.java index 828e97f81..c7c704935 100644 --- a/src/test/java/org/opensearch/ad/transport/MultiEntityResultTests.java +++ b/src/test/java/org/opensearch/ad/transport/MultiEntityResultTests.java @@ -132,11 +132,11 @@ import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; +import com.google.common.collect.ImmutableList; + import test.org.opensearch.ad.util.MLUtil; import test.org.opensearch.ad.util.RandomModelStateConfig; -import com.google.common.collect.ImmutableList; - public class MultiEntityResultTests extends AbstractTimeSeriesTest { private AnomalyResultTransportAction action; private AnomalyResultRequest request; @@ -581,8 +581,9 @@ private SearchResponse createEmptyResponse() { when(emptyComposite.getName()).thenReturn(CompositeRetriever.AGG_NAME_COMP); when(emptyComposite.afterKey()).thenReturn(null); // empty bucket - when(emptyComposite.getBuckets()) - .thenAnswer((Answer>) invocation -> { return new ArrayList(); }); + when(emptyComposite.getBuckets()).thenAnswer((Answer>) invocation -> { + return new ArrayList(); + }); Aggregations emptyAggs = new Aggregations(Collections.singletonList(emptyComposite)); SearchResponseSections emptySections = new SearchResponseSections(SearchHits.empty(), emptyAggs, null, false, null, null, 1); return new SearchResponse(emptySections, null, 1, 1, 0, 0, ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY); @@ -1014,8 +1015,9 @@ public void testNullFeatures() throws InterruptedException { when(emptyComposite.getName()).thenReturn(null); when(emptyComposite.afterKey()).thenReturn(null); // empty bucket - when(emptyComposite.getBuckets()) - .thenAnswer((Answer>) invocation -> { return new ArrayList(); }); + when(emptyComposite.getBuckets()).thenAnswer((Answer>) invocation -> { + return new ArrayList(); + }); Aggregations emptyAggs = new Aggregations(Collections.singletonList(emptyComposite)); SearchResponseSections emptySections = new SearchResponseSections(SearchHits.empty(), emptyAggs, null, false, null, null, 1); SearchResponse nullResponse = new SearchResponse(emptySections, null, 1, 1, 0, 0, ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY); @@ -1053,8 +1055,9 @@ public void testRetry() throws IOException, InterruptedException { when(emptyNonNullComposite.afterKey()).thenReturn(attrs3); List emptyNonNullCompositeBuckets = new ArrayList<>(); - when(emptyNonNullComposite.getBuckets()) - .thenAnswer((Answer>) invocation -> { return emptyNonNullCompositeBuckets; }); + when(emptyNonNullComposite.getBuckets()).thenAnswer((Answer>) invocation -> { + return emptyNonNullCompositeBuckets; + }); Aggregations emptyNonNullAggs = new Aggregations(Collections.singletonList(emptyNonNullComposite)); diff --git a/src/test/java/org/opensearch/ad/transport/ProfileTests.java b/src/test/java/org/opensearch/ad/transport/ProfileTests.java index 7d15fac77..7df0d5e02 100644 --- a/src/test/java/org/opensearch/ad/transport/ProfileTests.java +++ b/src/test/java/org/opensearch/ad/transport/ProfileTests.java @@ -43,11 +43,11 @@ import org.opensearch.test.OpenSearchTestCase; import org.opensearch.timeseries.constant.CommonName; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.JsonArray; import com.google.gson.JsonElement; +import test.org.opensearch.ad.util.JsonDeserializer; + public class ProfileTests extends OpenSearchTestCase { String node1, nodeName1, clusterName; String node2, nodeName2; diff --git a/src/test/java/org/opensearch/ad/transport/RCFPollingTests.java b/src/test/java/org/opensearch/ad/transport/RCFPollingTests.java index 9ba12535f..8cb592927 100644 --- a/src/test/java/org/opensearch/ad/transport/RCFPollingTests.java +++ b/src/test/java/org/opensearch/ad/transport/RCFPollingTests.java @@ -54,12 +54,12 @@ import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; -import test.org.opensearch.ad.util.FakeNode; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import test.org.opensearch.ad.util.FakeNode; +import test.org.opensearch.ad.util.JsonDeserializer; + public class RCFPollingTests extends AbstractTimeSeriesTest { Gson gson = new GsonBuilder().create(); private String detectorId = "jqIG6XIBEyaF3zCMZfcB"; diff --git a/src/test/java/org/opensearch/ad/transport/RCFResultTests.java b/src/test/java/org/opensearch/ad/transport/RCFResultTests.java index afc2d4824..58ceea32b 100644 --- a/src/test/java/org/opensearch/ad/transport/RCFResultTests.java +++ b/src/test/java/org/opensearch/ad/transport/RCFResultTests.java @@ -61,11 +61,11 @@ import org.opensearch.transport.Transport; import org.opensearch.transport.TransportService; -import test.org.opensearch.ad.util.JsonDeserializer; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import test.org.opensearch.ad.util.JsonDeserializer; + public class RCFResultTests extends OpenSearchTestCase { Gson gson = new GsonBuilder().create(); diff --git a/src/test/java/org/opensearch/forecast/indices/ForecastIndexManagementTests.java b/src/test/java/org/opensearch/forecast/indices/ForecastIndexManagementTests.java index cda346059..cdc19a4ac 100644 --- a/src/test/java/org/opensearch/forecast/indices/ForecastIndexManagementTests.java +++ b/src/test/java/org/opensearch/forecast/indices/ForecastIndexManagementTests.java @@ -99,24 +99,15 @@ public void testForecastResultIndexExists() throws IOException { public void testForecastResultIndexExistsAndNotRecreate() throws IOException { indices .initDefaultResultIndexIfAbsent( - TestHelpers - .createActionListener( - response -> logger.info("Acknowledged: " + response.isAcknowledged()), - failure -> { throw new RuntimeException("should not recreate index"); } - ) + TestHelpers.createActionListener(response -> logger.info("Acknowledged: " + response.isAcknowledged()), failure -> { + throw new RuntimeException("should not recreate index"); + }) ); TestHelpers.waitForIndexCreationToComplete(client(), ForecastIndex.RESULT.getIndexName()); if (client().admin().indices().prepareExists(ForecastIndex.RESULT.getIndexName()).get().isExists()) { - indices - .initDefaultResultIndexIfAbsent( - TestHelpers - .createActionListener( - response -> { throw new RuntimeException("should not recreate index " + ForecastIndex.RESULT.getIndexName()); }, - failure -> { - throw new RuntimeException("should not recreate index " + ForecastIndex.RESULT.getIndexName(), failure); - } - ) - ); + indices.initDefaultResultIndexIfAbsent(TestHelpers.createActionListener(response -> { + throw new RuntimeException("should not recreate index " + ForecastIndex.RESULT.getIndexName()); + }, failure -> { throw new RuntimeException("should not recreate index " + ForecastIndex.RESULT.getIndexName(), failure); })); } } @@ -168,11 +159,9 @@ public void testCustomResultIndexExists() throws IOException { indices .initCustomResultIndexDirectly( indexName, - TestHelpers - .createActionListener( - response -> logger.info("Acknowledged: " + response.isAcknowledged()), - failure -> { throw new RuntimeException("should not recreate index"); } - ) + TestHelpers.createActionListener(response -> logger.info("Acknowledged: " + response.isAcknowledged()), failure -> { + throw new RuntimeException("should not recreate index"); + }) ); TestHelpers.waitForIndexCreationToComplete(client(), indexName); assertTrue((client().admin().indices().prepareExists(indexName).get().isExists())); @@ -331,11 +320,9 @@ public void testInitCustomResultIndexAndExecuteIndex() throws InterruptedExcepti indices .initCustomResultIndexDirectly( indexName, - TestHelpers - .createActionListener( - response -> logger.info("Acknowledged: " + response.isAcknowledged()), - failure -> { throw new RuntimeException("should not recreate index"); } - ) + TestHelpers.createActionListener(response -> logger.info("Acknowledged: " + response.isAcknowledged()), failure -> { + throw new RuntimeException("should not recreate index"); + }) ); TestHelpers.waitForIndexCreationToComplete(client(), indexName); CountDownLatch latch = new CountDownLatch(1); diff --git a/src/test/java/org/opensearch/timeseries/dataprocessor/SingleFeatureLinearUniformImputerTests.java b/src/test/java/org/opensearch/timeseries/dataprocessor/SingleFeatureLinearUniformImputerTests.java index 17aae0422..0bf7bdffb 100644 --- a/src/test/java/org/opensearch/timeseries/dataprocessor/SingleFeatureLinearUniformImputerTests.java +++ b/src/test/java/org/opensearch/timeseries/dataprocessor/SingleFeatureLinearUniformImputerTests.java @@ -15,13 +15,13 @@ import java.util.Arrays; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @RunWith(JUnitParamsRunner.class) public class SingleFeatureLinearUniformImputerTests { diff --git a/src/test/java/org/opensearch/timeseries/dataprocessor/ZeroImputerTests.java b/src/test/java/org/opensearch/timeseries/dataprocessor/ZeroImputerTests.java index a13189db2..8e03821e2 100644 --- a/src/test/java/org/opensearch/timeseries/dataprocessor/ZeroImputerTests.java +++ b/src/test/java/org/opensearch/timeseries/dataprocessor/ZeroImputerTests.java @@ -7,13 +7,13 @@ import static org.junit.Assert.assertArrayEquals; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + @RunWith(JUnitParamsRunner.class) public class ZeroImputerTests { diff --git a/src/test/java/org/opensearch/timeseries/feature/NoPowermockSearchFeatureDaoTests.java b/src/test/java/org/opensearch/timeseries/feature/NoPowermockSearchFeatureDaoTests.java index 307740361..49745e565 100644 --- a/src/test/java/org/opensearch/timeseries/feature/NoPowermockSearchFeatureDaoTests.java +++ b/src/test/java/org/opensearch/timeseries/feature/NoPowermockSearchFeatureDaoTests.java @@ -343,8 +343,9 @@ public void testGetHighestCountEntitiesExhaustedPages() throws InterruptedExcept when(emptyComposite.getName()).thenReturn(SearchFeatureDao.AGG_NAME_TOP); when(emptyComposite.afterKey()).thenReturn(null); // empty bucket - when(emptyComposite.getBuckets()) - .thenAnswer((Answer>) invocation -> { return new ArrayList(); }); + when(emptyComposite.getBuckets()).thenAnswer((Answer>) invocation -> { + return new ArrayList(); + }); Aggregations emptyAggs = new Aggregations(Collections.singletonList(emptyComposite)); SearchResponseSections emptySections = new SearchResponseSections(SearchHits.empty(), emptyAggs, null, false, null, null, 1); SearchResponse emptyResponse = new SearchResponse(emptySections, null, 1, 1, 0, 0, ShardSearchFailure.EMPTY_ARRAY, Clusters.EMPTY); diff --git a/src/test/java/org/opensearch/timeseries/feature/SearchFeatureDaoParamTests.java b/src/test/java/org/opensearch/timeseries/feature/SearchFeatureDaoParamTests.java index 87d00efce..d2702d7cb 100644 --- a/src/test/java/org/opensearch/timeseries/feature/SearchFeatureDaoParamTests.java +++ b/src/test/java/org/opensearch/timeseries/feature/SearchFeatureDaoParamTests.java @@ -35,9 +35,6 @@ import java.util.Optional; import java.util.concurrent.ExecutorService; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; - import org.apache.lucene.search.TotalHits; import org.junit.Before; import org.junit.Test; @@ -90,6 +87,9 @@ import com.google.gson.Gson; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + /** * Due to https://tinyurl.com/2y265s2w, tests with and without @Parameters annotation * are incompatible with each other. This class tests SearchFeatureDao using @Parameters,