@@ -39,10 +39,10 @@ import org.opensearch.indexmanagement.transform.model.ShardNewDocuments
3939import org.opensearch.indexmanagement.transform.model.Transform
4040import org.opensearch.indexmanagement.transform.model.TransformSearchResult
4141import org.opensearch.indexmanagement.transform.model.TransformStats
42+ import org.opensearch.indexmanagement.transform.opensearchapi.retryTransformSearch
4243import org.opensearch.indexmanagement.transform.settings.TransformSettings.Companion.TRANSFORM_JOB_SEARCH_BACKOFF_COUNT
4344import org.opensearch.indexmanagement.transform.settings.TransformSettings.Companion.TRANSFORM_JOB_SEARCH_BACKOFF_MILLIS
4445import org.opensearch.indexmanagement.transform.util.TransformContext
45- import org.opensearch.indexmanagement.transform.util.TransformLockManager
4646import org.opensearch.indexmanagement.util.IndexUtils.Companion.LUCENE_MAX_CLAUSES
4747import org.opensearch.indexmanagement.util.IndexUtils.Companion.ODFE_MAGIC_NULL
4848import org.opensearch.indexmanagement.util.IndexUtils.Companion.hashToFixedSize
@@ -61,6 +61,7 @@ import org.opensearch.search.aggregations.metrics.Percentiles
6161import org.opensearch.search.aggregations.metrics.ScriptedMetric
6262import org.opensearch.search.builder.SearchSourceBuilder
6363import org.opensearch.transport.RemoteTransportException
64+ import java.time.Instant
6465import java.util.concurrent.TimeUnit
6566import kotlin.math.max
6667import kotlin.math.pow
@@ -114,25 +115,39 @@ class TransformSearchService(
114115 }
115116
116117 @Suppress(" RethrowCaughtException" )
117- suspend fun getShardLevelModifiedBuckets (transform : Transform , afterKey : Map <String , Any >? , currentShard : ShardNewDocuments , transformContext : TransformContext ): BucketSearchResult {
118+ suspend fun getShardLevelModifiedBuckets (
119+ transform : Transform ,
120+ afterKey : Map <String , Any >? ,
121+ currentShard : ShardNewDocuments ,
122+ transformContext : TransformContext
123+ ): BucketSearchResult {
118124 try {
119125 var retryAttempt = 0
120126 var pageSize = calculateMaxPageSize(transform)
121- val searchResponse = backoffPolicy.retry(logger, transformContext.transformLockManager) {
127+ val searchStart = Instant .now().epochSecond
128+ val searchResponse = backoffPolicy.retryTransformSearch(logger, transformContext.transformLockManager) {
122129 val pageSizeDecay = 2f .pow(retryAttempt++ )
130+ val searchRequestTimeoutInSeconds = transformContext.getMaxRequestTimeoutInSeconds()
123131 client.suspendUntil { listener: ActionListener <SearchResponse > ->
124- pageSize = transformContext.pageSize ? : max(1 , pageSize.div(pageSizeDecay.toInt()))
132+ // If the previous request of the current transform job execution was successful, take the page size of previous request.
133+ // If not, calculate the page size.
134+ pageSize = transformContext.lastSuccessfulPageSize ? : max(1 , pageSize.div(pageSizeDecay.toInt()))
125135 if (retryAttempt > 1 ) {
126136 logger.debug(
127137 " Attempt [${retryAttempt - 1 } ] to get modified buckets for transform [${transform.id} ]. Attempting " +
128138 " again with reduced page size [$pageSize ]"
129139 )
130140 }
131- val request = getShardLevelBucketsSearchRequest(transform, afterKey, pageSize, currentShard, transformContext.getMaxRequestTimeout())
141+ if (searchRequestTimeoutInSeconds == null ) {
142+ return @suspendUntil
143+ }
144+ val request = getShardLevelBucketsSearchRequest(transform, afterKey, pageSize, currentShard, searchRequestTimeoutInSeconds)
132145 search(request, listener)
133146 }
134147 }
135- transformContext.pageSize = pageSize
148+ // If the request was successful, update page size
149+ transformContext.lastSuccessfulPageSize = pageSize
150+ transformContext.renewLockForLongSearch(Instant .now().epochSecond - searchStart)
136151 return convertBucketSearchResponse(transform, searchResponse)
137152 } catch (e: TransformSearchServiceException ) {
138153 throw e
@@ -169,22 +184,28 @@ class TransformSearchService(
169184 modifiedBuckets.size
170185
171186 var retryAttempt = 0
172- val searchResponse = backoffPolicy.retry(logger, transformContext.transformLockManager) {
173- // TODO: Should we store the value of the past successful page size (?)
174- val pageSizeDecay = 2f .pow(retryAttempt++ )
187+ val searchStart = Instant .now().epochSecond
188+ val searchResponse = backoffPolicy.retryTransformSearch(logger, transformContext.transformLockManager) {
189+ val pageSizeDecay = 2f .pow(retryAttempt++ )
190+ val searchRequestTimeoutInSeconds = transformContext.getMaxRequestTimeoutInSeconds()
191+
175192 client.suspendUntil { listener: ActionListener <SearchResponse > ->
176- pageSize = transformContext.pageSize ? : max(1 , pageSize.div(pageSizeDecay.toInt()))
193+ // If the previous request of the current transform job execution was successful, take the page size of previous request.
194+ // If not, calculate the page size.
195+ pageSize = transformContext.lastSuccessfulPageSize ? : max(1 , pageSize.div(pageSizeDecay.toInt()))
177196 if (retryAttempt > 1 ) {
178197 logger.debug(
179198 " Attempt [${retryAttempt - 1 } ] of composite search failed for transform [${transform.id} ]. Attempting " +
180199 " again with reduced page size [$pageSize ]"
181200 )
182201 }
183- val request = getSearchServiceRequest(transform, afterKey, pageSize, modifiedBuckets, transformContext.getMaxRequestTimeout() )
202+ val request = getSearchServiceRequest(transform, afterKey, pageSize, modifiedBuckets, searchRequestTimeoutInSeconds )
184203 search(request, listener)
185204 }
186205 }
187- transformContext.pageSize = pageSize
206+ // If the request was successful, update page size
207+ transformContext.lastSuccessfulPageSize = pageSize
208+ transformContext.renewLockForLongSearch(Instant .now().epochSecond - searchStart)
188209 return convertResponse(transform, searchResponse, modifiedBuckets = modifiedBuckets)
189210 } catch (e: TransformSearchServiceException ) {
190211 throw e
@@ -210,7 +231,7 @@ class TransformSearchService(
210231 afterKey : Map <String , Any >? = null,
211232 pageSize : Int ,
212233 modifiedBuckets : MutableSet <Map <String , Any >>? = null,
213- timeout : Long? = null
234+ timeoutInSeconds : Long? = null
214235 ): SearchRequest {
215236 val sources = mutableListOf<CompositeValuesSourceBuilder <* >>()
216237 transform.groups.forEach { group -> sources.add(group.toSourceBuilder().missingBucket(true )) }
@@ -223,7 +244,7 @@ class TransformSearchService(
223244 } else {
224245 getQueryWithModifiedBuckets(transform.dataSelectionQuery, modifiedBuckets, transform.groups)
225246 }
226- return getSearchServiceRequest(transform.sourceIndex, query, aggregationBuilder, timeout )
247+ return getSearchServiceRequest(transform.sourceIndex, query, aggregationBuilder, timeoutInSeconds )
227248 }
228249
229250 private fun getQueryWithModifiedBuckets (
@@ -251,7 +272,24 @@ class TransformSearchService(
251272 return query
252273 }
253274
254- private fun getSearchServiceRequest (index : String , query : QueryBuilder , aggregationBuilder : CompositeAggregationBuilder , timeout : Long? = null): SearchRequest {
275+ /* *
276+ * Creates transform search request and sets timeout if it is provided
277+ * Referring on: https://github.com/opensearch-project/OpenSearch/pull/1085
278+ * https://github.com/opensearch-project/documentation-website/blob/main/_opensearch/rest-api/search.md#url-parameters
279+ * cancel_after_time_interval property is used in order to set timeout of transform search request has not been ported to version 1.0
280+ * thus we can't use it for version 1.0 support
281+ *
282+ * @param index - index that will be searched
283+ * @param query - any additional [RestStatus] values that should be retried
284+ * @param aggregationBuilder - search aggregations
285+ * @param timeoutInSeconds - timeout period used for transform search request
286+ */
287+ private fun getSearchServiceRequest (
288+ index : String ,
289+ query : QueryBuilder ,
290+ aggregationBuilder : CompositeAggregationBuilder ,
291+ timeoutInSeconds : Long? = null
292+ ): SearchRequest {
255293 val searchSourceBuilder = SearchSourceBuilder ()
256294 .trackTotalHits(false )
257295 .size(0 )
@@ -260,10 +298,9 @@ class TransformSearchService(
260298 val request = SearchRequest (index)
261299 .source(searchSourceBuilder)
262300 .allowPartialSearchResults(false )
263-
264- if (timeout != null ){
265- request.cancelAfterTimeInterval = TimeValue (timeout, TimeUnit .SECONDS )
266- }
301+ // The time after which the search request will be canceled.
302+ // Request-level parameter takes precedence over cancel_after_time_interval cluster setting. Default is -1.
303+ request.cancelAfterTimeInterval = timeoutInSeconds?.let { TimeValue (timeoutInSeconds, TimeUnit .SECONDS ) }
267304 return request
268305 }
269306
@@ -272,15 +309,15 @@ class TransformSearchService(
272309 afterKey : Map <String , Any >? = null,
273310 pageSize : Int ,
274311 currentShard : ShardNewDocuments ,
275- timeout : Long?
312+ timeoutInSeconds : Long?
276313 ): SearchRequest {
277314 val rangeQuery = getSeqNoRangeQuery(currentShard.from, currentShard.to)
278315 val query = QueryBuilders .boolQuery().filter(rangeQuery).must(transform.dataSelectionQuery)
279316 val sources = transform.groups.map { it.toSourceBuilder().missingBucket(true ) }
280317 val aggregationBuilder = CompositeAggregationBuilder (transform.id, sources)
281318 .size(pageSize)
282319 .apply { afterKey?.let { this .aggregateAfter(it) } }
283- return getSearchServiceRequest(currentShard.shardId.indexName, query, aggregationBuilder, timeout )
320+ return getSearchServiceRequest(currentShard.shardId.indexName, query, aggregationBuilder, timeoutInSeconds )
284321 .preference(" _shards:" + currentShard.shardId.id.toString())
285322 }
286323
0 commit comments