Skip to content

Conversation

@RS146BIJAY
Copy link
Contributor

@RS146BIJAY RS146BIJAY commented Dec 2, 2025

Description

Fixing indexing regression and bug fixes for grouping criteria. For testing grouping criteria changes, enabled the grouping criteria on local and tested with setting criteria. Wil raise the changes for integ test enablement for CAS in a separate PR as that require decent changes in integ test as well.

Related Issues

#19919

Check List

  • Functionality includes testing.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an indexing regression and issues with grouping criteria.
    • Adjusted retry/lock acquisition behavior and raised default/max retry thresholds for better resilience.
    • Narrowed returned composite field types to only the intended subtype.
  • Tests

    • Added and updated tests to validate retry/lock-acquisition limits and retry behavior.
    • Removed obsolete tests covering the prior retry branch.
  • Documentation

    • Updated changelog with the above fixes.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Walkthrough

Adjusts lookup-map lock retry behavior: raises default retry limit, propagates a max-retry parameter into CompositeIndexWriter's lookup path using bounded tryAcquire loops, removes exception-driven retry from bulk action, narrows a mapper return set, updates tests and CHANGELOG entries. (≤50 words)

Changes

Cohort / File(s) Change Summary
Configuration Updates
server/src/main/java/org/opensearch/index/IndexSettings.java
Increases INDEX_MAX_RETRY_ON_LOOKUP_MAP_LOCK_ACQUISITION_EXCEPTION default from 15 → 100 and max bound from 100 → 500 (min remains 5).
Composite writer refactor
server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java
Changes inner-class visibilities and mapReadLock to package-private, removes getCurrentMap() helper, replaces map acquisition with bounded tryAcquire() retry loops, adds maxRetryOnLookupMapAcquisitionException parameter propagation to computeIndexWriterIfAbsentForCriteria, and adjusts iteration/metric paths to use concrete writer instances.
Bulk action retry removal
server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java
Removes import and handling for LookupMapLockAcquisitionException, deletes isLookupMapLockAcquisitionException helper, and removes the retry branch that relied on that exception.
Mapper filtering change
server/src/main/java/org/opensearch/index/mapper/MapperService.java
getCompositeFieldTypes() now filters compositeMappedFieldTypes to return only instances of StarTreeMapper.StarTreeFieldType via stream+collect.
Tests — removals
server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java
Removes two tests exercising LookupMapLockAcquisitionException retry scenarios and related static imports.
Tests — additions/updates
server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java
Updates calls to computeIndexWriterIfAbsentForCriteria to pass MAX_NUMBER_OF_RETRIES; adds testMaxRetryCountWhenWriteLockDuringIndexing() which mocks failed tryAcquire() and asserts a LookupMapLockAcquisitionException after max retries and verifies retry count.
Test base constant
server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java
Adds public static final MAX_NUMBER_OF_RETRIES = 20.
Changelog
CHANGELOG.md
Adds Unreleased 3.x Fixed entries: fix toBuilder in EngineConfig to include mergedSegmentTransferTracker, and fixes for indexing regression and grouping-criteria bugs (PR #20145).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • CompositeIndexWriter.java: correctness of bounded tryAcquire retry loop, lock acquisition/release semantics, and error/close paths.
    • Call sites updated to pass maxRetryOnLookupMapAcquisitionException: ensure consistent propagation and behavior.
    • TransportShardBulkAction.java: verify removal of exception-driven retry is safe.
    • Tests: confirm mocks and new MAX_NUMBER_OF_RETRIES align with production retry configuration.

Poem

🐇 I nibble at locks and count each try,

Bounded hops beneath a careful sky,
Streams pruned tight, retries set anew,
Tests hop forward — steady, true.
A rabbit cheers the view!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: fixing an indexing regression and applying bug fixes for grouping criteria, which aligns with the file modifications and the PR's stated objectives.
Description check ✅ Passed The description covers the main purpose, testing approach, and related issue. However, it lacks specific details about what the regression was or which specific bugs were fixed, and only two of three checklist items are marked complete.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (2)

215-215: Reduced encapsulation of mapReadLock.

The visibility of mapReadLock has been changed from private final to package-private, allowing direct access from other classes in the same package. This field controls critical concurrency behavior, and exposing it directly increases the risk of misuse.

Consider:

  1. Keeping the field private and exposing only necessary operations through methods (e.g., tryAcquireLock()).
  2. If package-private access is required for the retry logic, add clear documentation about proper usage patterns and thread-safety requirements.
  3. Restrict access using a package-private accessor method rather than exposing the field directly.

498-498: Simplify boolean comparisons.

The condition uses explicit == false and == true comparisons which are redundant in Java.

Apply this diff:

-if (success == false && current != null && current.mapReadLock.isHeldByCurrentThread() == true) {
+if (!success && current != null && current.mapReadLock.isHeldByCurrentThread()) {
     current.mapReadLock.close();
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f76826c and 1d42f98.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1 hunks)
  • server/src/main/java/org/opensearch/index/IndexSettings.java (1 hunks)
  • server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (10 hunks)
  • server/src/main/java/org/opensearch/index/mapper/MapperService.java (2 hunks)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java (0 hunks)
  • server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (4 hunks)
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java (1 hunks)
💤 Files with no reviewable changes (1)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: gradle-check
🔇 Additional comments (13)
server/src/main/java/org/opensearch/index/mapper/MapperService.java (2)

87-87: LGTM!

Import correctly added to support the stream operations in getCompositeFieldTypes().


693-697: Verify the behavior change scope and call frequency.

The filtering to return only StarTreeMapper.StarTreeFieldType instances represents a narrowed scope from returning all composite field types. Confirm this change is intentional and whether any callers expect other CompositeMappedFieldType implementations. Additionally, verify the call frequency of this method; if invoked on hot paths, consider caching the filtered result to avoid repeated stream collection operations.

server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java (1)

106-106: LGTM!

The test constant is appropriately set to a lower value (20) than the production default (100) for faster test execution while still being within the valid range (5-500).

server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (5)

44-46: LGTM!

Mockito imports correctly added to support the new verification test.


71-77: LGTM!

Method call correctly updated to include MAX_NUMBER_OF_RETRIES parameter, aligning with the new bounded retry API.


141-146: LGTM!

Method call correctly updated with retry parameter.


197-202: LGTM!

Method call correctly updated with retry parameter.


208-227: Test validates bounded retry semantics correctly.

The test properly verifies:

  1. LookupMapLockAcquisitionException is thrown after exhausting retries
  2. tryAcquire() is called exactly MAX_NUMBER_OF_RETRIES times

One consideration: the mock setup directly assigns to map.current and map.current.mapReadLock which accesses package-private fields. This works for testing but creates tight coupling to internal implementation details.

server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1)

724-753: Retry logic moved to lower layer - verify exception handling.

The LookupMapLockAcquisitionException retry logic has been removed from bulk action handling and moved to CompositeIndexWriter with bounded retries. This architectural approach places retry logic closer to where the exception originates.

Ensure that when LookupMapLockAcquisitionException propagates up after max retries are exhausted, it's properly handled and doesn't cause unexpected bulk operation failures.

server/src/main/java/org/opensearch/index/IndexSettings.java (1)

499-506: Significant default value change - verify upgrade impact.

The default retry count increased to 100 with a maximum of 500. Since this is a dynamic setting, existing indices will apply the new default upon upgrade. Consider whether this change should be documented in release notes for operators who have tuned their clusters based on previous defaults.

server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (3)

691-693: LGTM: Metrics gathering refactoring.

The refactoring from stream-based iteration to explicit for-loops improves code clarity and performance for these simple aggregation operations. The logic is correct in all cases, with proper handling of both current and old maps where necessary, and appropriate locking in ramBytesUsed().

Also applies to: 702-704, 731-742, 758-770, 796-806


210-210: Verify removal of final modifier is intentional.

The final modifier has been removed from CriteriaBasedIndexWriterLookup, CriteriaBasedWriterLock, and LiveIndexWriterDeletesMap. This allows subclassing of these internal implementation classes. Confirm whether:

  1. Subclassing is required for test mocking/stubbing.
  2. If so, consider restricting visibility to test scope or use sealed classes.
  3. If intentional for production extensibility, document extension points and invariants.

Also applies to: 301-301, 406-406


678-679: Verify retry configuration defaults and bounds.

The maxRetryOnLookupMapAcquisitionException setting controls retry behavior for lookup map acquisition. Without access to the codebase, I cannot confirm the specific default values, upper/lower bounds, or whether they align with industry best practices for lock acquisition retry mechanisms. Ensure the following:

  • Default retry count is reasonable (typically 3–10 attempts for in-thread locks per best practices)
  • Upper bounds prevent excessive retries that could cause performance degradation
  • Configuration is documented and tunable per workload expectations
  • Retry strategy includes backoff/jitter to avoid retry storms (not just blind spinning)

CHANGELOG.md Outdated
- Fix node bootstrap error when enable stream transport and remote cluster state ([#19948](https://github.com/opensearch-project/OpenSearch/pull/19948))
- Fix deletion failure/error of unused index template; case when an index template matches a data stream but has a lower priority. ([#20102](https://github.com/opensearch-project/OpenSearch/pull/20102))
- Fix toBuilder method in EngineConfig to include mergedSegmentTransferTracker([20105](https://github.com/opensearch-project/OpenSearch/pull/20105))
- Fix indexing regression and bug fixes for grouping criteria([20145](https://github.com/opensearch-project/OpenSearch/pull/20145))
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor formatting inconsistency.

The PR link format is inconsistent with other entries in the changelog. Missing # before the PR number.

Apply this diff to fix the formatting:

-- Fix indexing regression and bug fixes for grouping criteria([20145](https://github.com/opensearch-project/OpenSearch/pull/20145))
+- Fix indexing regression and bug fixes for grouping criteria ([#20145](https://github.com/opensearch-project/OpenSearch/pull/20145))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Fix indexing regression and bug fixes for grouping criteria([20145](https://github.com/opensearch-project/OpenSearch/pull/20145))
- Fix indexing regression and bug fixes for grouping criteria ([#20145](https://github.com/opensearch-project/OpenSearch/pull/20145))
🤖 Prompt for AI Agents
CHANGELOG.md around line 94: the PR link is missing the '#' before the PR number
which makes it inconsistent with other entries; update the link by inserting '#'
before the PR number so it reads .../pull/#20145 (i.e. change
(https://github.com/opensearch-project/OpenSearch/pull/20145) to include the
hash) and ensure the rest of the entry formatting matches surrounding lines.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (1)

475-489: Add exponential backoff to retry loop.

The retry loop performs a busy-wait by calling tryAcquire() repeatedly without any delay between attempts. This pattern wastes CPU cycles under contention when the lock is frequently unavailable.

As suggested in the previous review, add exponential backoff between retry attempts with Thread.sleep(Math.min(1L << counter, 100L)) and handle InterruptedException by restoring the interrupt flag and propagating LookupMapLockAcquisitionException.

🧹 Nitpick comments (1)
server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (1)

483-483: Simplify boolean comparisons.

The expressions current.isClosed() == true (line 483) and current.mapReadLock.isHeldByCurrentThread() == true (line 498) contain redundant boolean comparisons.

Apply this diff to simplify:

-                    if (current != null && current.isClosed() == true) {
+                    if (current != null && current.isClosed()) {
-                if (success == false && current != null && current.mapReadLock.isHeldByCurrentThread() == true) {
+                if (!success && current != null && current.mapReadLock.isHeldByCurrentThread()) {

Also applies to: 498-498

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d42f98 and 4b247a0.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1 hunks)
  • server/src/main/java/org/opensearch/index/IndexSettings.java (1 hunks)
  • server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (10 hunks)
  • server/src/main/java/org/opensearch/index/mapper/MapperService.java (2 hunks)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java (0 hunks)
  • server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (4 hunks)
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java (1 hunks)
💤 Files with no reviewable changes (1)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java
🚧 Files skipped from review as they are similar to previous changes (5)
  • server/src/main/java/org/opensearch/index/mapper/MapperService.java
  • CHANGELOG.md
  • server/src/main/java/org/opensearch/index/IndexSettings.java
  • server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java
🔇 Additional comments (6)
server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1)

717-731: Behavior change: retries now only on version conflicts in onComplete

The retry path in onComplete now exclusively handles VersionConflictEngineException (via isConflictException) bounded by retryOnConflict, with the previous LookupMapLockAcquisitionException-specific retry branch removed. That seems consistent with moving lock-acquisition retry logic down into CompositeIndexWriter and keeps this layer from depending on engine-internal exception types.

Please double-check that:

  • LookupMapLockAcquisitionException is fully retried/consumed within CompositeIndexWriter (or below), and
  • it does not bubble back up to this point expecting a retry here anymore,

so we don’t inadvertently change failure behavior for callers that previously relied on the bulk action to transparently retry on transient lookup-map lock contention.

server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (5)

210-210: Visibility changes support the new retry mechanism.

The removal of final modifiers from inner classes and the change of mapReadLock to package-private access are necessary to support the refactored retry logic. The package-private access on mapReadLock (line 215) enables LiveIndexWriterDeletesMap.computeIndexWriterIfAbsentForCriteria to call tryAcquire() directly at line 482.

Also applies to: 215-215, 301-301, 406-406, 408-408


466-471: Proper parameter propagation for configurable retry limit.

The addition of maxRetryOnLookupMapAcquisitionException parameter enables configurable retry behavior, and the value is correctly sourced from engineConfig.getIndexSettings().

Also applies to: 675-680


691-693: Simplified iteration improves readability.

The refactoring from stream-based iteration to direct iteration over criteriaBasedIndexWriterMap.values() is cleaner and avoids unnecessary intermediate operations.

Also applies to: 702-704


731-750: Proper tragic exception checking across all writers.

The iteration through both current and old writer maps to detect tragic exceptions is thorough and correctly checks if writers are closed before accessing their tragic exception state.


753-774: Correct synchronization and state checks.

The ramBytesUsed() method properly acquires write locks before iterating, and the rollback() method correctly checks if writers are open before attempting rollback operations.

Also applies to: 794-811

@github-actions
Copy link
Contributor

github-actions bot commented Dec 2, 2025

❌ Gradle check result for 4b247a0: null

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (1)

44-46: Retry wiring in tests looks correct; consider simplifying the mocking for readability.

  • Updating all computeIndexWriterIfAbsentForCriteria invocations to pass MAX_NUMBER_OF_RETRIES keeps the tests consistent with the new API and the retry semantics; this looks correct.
  • testMaxRetryCountWhenWriteLockDuringIndexing correctly verifies that tryAcquire() is invoked exactly MAX_NUMBER_OF_RETRIES times when the lock is never obtained, and the LookupMapLockAcquisitionException is thrown as expected.

As a minor test ergonomics tweak, you could stub and verify directly on writerLock instead of going through map.current.mapReadLock in the when(...) and verify(...) calls. That would make the test a bit less coupled to the internal layout of LiveIndexWriterDeletesMap and CriteriaBasedIndexWriterLookup while preserving the behavior being asserted.

Also applies to: 72-77, 141-146, 197-202, 208-227

server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (1)

210-215: Visibility and mutability changes for nested types are acceptable but could use an explicit “for testing” annotation.

Making CriteriaBasedIndexWriterLookup and CriteriaBasedWriterLock more visible, and relaxing mapReadLock and LiveIndexWriterDeletesMap.current from final, is understandable to support the new tests that need to mock and override these internals.

To keep the public surface area tidy and signal intent, consider adding an explicit @opensearch.internal (or similar) Javadoc tag or comment on these nested types/fields indicating that they are exposed primarily for testing. That helps discourage external production code from depending on them and makes future refactors easier.

Also applies to: 301-301, 406-412

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b247a0 and dbcae67.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1 hunks)
  • server/src/main/java/org/opensearch/index/IndexSettings.java (1 hunks)
  • server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (10 hunks)
  • server/src/main/java/org/opensearch/index/mapper/MapperService.java (2 hunks)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java (0 hunks)
  • server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (4 hunks)
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java (1 hunks)
💤 Files with no reviewable changes (1)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java
  • server/src/main/java/org/opensearch/index/IndexSettings.java
  • CHANGELOG.md
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: gradle-check
  • GitHub Check: precommit (21, windows-2025, true)
  • GitHub Check: precommit (25, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, ubuntu-latest)
  • GitHub Check: precommit (25, macos-15)
  • GitHub Check: precommit (25, macos-15-intel)
  • GitHub Check: precommit (25, windows-latest)
  • GitHub Check: precommit (21, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, macos-15)
  • GitHub Check: precommit (25, ubuntu-latest)
  • GitHub Check: precommit (21, windows-latest)
  • GitHub Check: precommit (21, macos-15-intel)
  • GitHub Check: assemble (25, windows-latest)
  • GitHub Check: Analyze (java)
  • GitHub Check: assemble (21, windows-latest)
  • GitHub Check: assemble (25, ubuntu-24.04-arm)
  • GitHub Check: assemble (21, ubuntu-24.04-arm)
  • GitHub Check: assemble (21, ubuntu-latest)
  • GitHub Check: assemble (25, ubuntu-latest)
  • GitHub Check: detect-breaking-change
🔇 Additional comments (3)
server/src/main/java/org/opensearch/index/mapper/MapperService.java (2)

87-87: LGTM!

The import is necessary for the Collectors.toSet() operation used in the getCompositeFieldTypes() method below.


694-696: Verify initialization safety, caller expectations, and method naming alignment.

The method now filters to return only StarTreeMapper.StarTreeFieldType instances from compositeMappedFieldTypes:

  1. Potential NPE risk: Verify that getCompositeFieldTypes() is never called before internalMerge() initializes compositeMappedFieldTypes at line 552. If called during early initialization phases, .stream() could fail on a null reference.

  2. Semantic narrowing: Confirm whether the method name getCompositeFieldTypes() still accurately reflects its behavior. If other composite field type implementations exist or may be added, consider renaming to getStarTreeFieldTypes() or updating documentation to clarify the filtering behavior.

  3. Performance: If getCompositeFieldTypes() is called frequently in hot paths, consider caching the filtered result to avoid recreating the set on each invocation.

server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (1)

691-705: Iteration refactors over current/old writer maps look correct and improve clarity.

  • getFlushingBytes() and getPendingNumDocs() now iterate directly over liveIndexWriterDeletesMap.current.criteriaBasedIndexWriterMap.values(), summing per-child metrics before adding the accumulating writer’s values. This preserves behavior and is straightforward.
  • getTragicException() now checks both current and old child writers for a tragic exception before falling back to the accumulating writer, which ensures group-specific failures are surfaced.
  • ramBytesUsed() and rollback() explicitly iterate over both current and old writers, and the use of mapWriteLock.acquire() around the ramBytesUsed() traversals is appropriate for a consistent snapshot.

Overall, these loops are clear and consistent with the data structures being used; no issues from a correctness or concurrency standpoint.

Also applies to: 731-742, 757-772, 796-805

@github-actions
Copy link
Contributor

github-actions bot commented Dec 3, 2025

❌ Gradle check result for dbcae67: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
server/src/main/java/org/opensearch/index/mapper/MapperService.java (2)

694-696: Consider renaming method to reflect StarTree-specific filtering.

The method name getCompositeFieldTypes() suggests it returns all composite field types, but it now filters to return only StarTreeMapper.StarTreeFieldType instances. This could mislead callers who expect all composite types.

Consider renaming to getStarTreeFieldTypes() or adding a clarifying comment:

+    /**
+     * Returns only StarTree composite field types.
+     * For all composite field types, use getCompositeFieldTypesFromMapper().
+     */
     public Set<CompositeMappedFieldType> getCompositeFieldTypes() {
         return compositeMappedFieldTypes.stream()
             .filter(compositeMappedFieldType -> compositeMappedFieldType instanceof StarTreeMapper.StarTreeFieldType)
             .collect(Collectors.toSet());
     }

694-696: Consider caching the filtered result to avoid repeated stream operations.

The method creates a new stream, filters, and collects to a Set on every invocation. Since compositeMappedFieldTypes only changes during merge operations (line 552), the filtered result could be cached in a separate volatile field and updated alongside compositeMappedFieldTypes.

Example optimization:

 private volatile Set<CompositeMappedFieldType> compositeMappedFieldTypes;
+private volatile Set<CompositeMappedFieldType> starTreeFieldTypes;
 
 // In internalMerge() after line 552:
 this.compositeMappedFieldTypes = getCompositeFieldTypesFromMapper();
+this.starTreeFieldTypes = compositeMappedFieldTypes.stream()
+    .filter(type -> type instanceof StarTreeMapper.StarTreeFieldType)
+    .collect(Collectors.toSet());
 
 public Set<CompositeMappedFieldType> getCompositeFieldTypes() {
-    return compositeMappedFieldTypes.stream()
-        .filter(compositeMappedFieldType -> compositeMappedFieldType instanceof StarTreeMapper.StarTreeFieldType)
-        .collect(Collectors.toSet());
+    return starTreeFieldTypes;
 }
server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (1)

475-489: Consider adding exponential backoff between retry attempts.

The retry loop repeatedly calls tryAcquire() without delay, creating a busy-wait pattern. With a default max retry of 100 (configurable up to 500), this can waste significant CPU cycles under contention.

A past review comment suggested adding exponential backoff, but the current code doesn't implement it. Consider adding a small delay (e.g., Thread.sleep(Math.min(1L << counter, 100L))) between attempts, with appropriate InterruptedException handling.

 int counter = 0;
 while ((current == null || current.isClosed()) && counter < maxRetryOnLookupMapAcquisitionException) {
     // This function acquires a first read lock on a map which does not have any write lock present. Current keeps
     // on getting rotated during refresh, so there will be one current on which read lock can be obtained.
     // Validate that no write lock is applied on the map and the map is not closed. Idea here is write lock was
     // never applied on this map as write lock gets only during closing time. We are doing this instead of acquire,
     // because acquire can also apply a read lock in case refresh completed and map is closed.
     current = this.current.mapReadLock.tryAcquire();
     if (current != null && current.isClosed() == true) {
         current.mapReadLock.close();
         current = null;
     }

+    if (current == null && counter < maxRetryOnLookupMapAcquisitionException - 1) {
+        try {
+            Thread.sleep(Math.min(1L << counter, 100L));
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new LookupMapLockAcquisitionException(shardId, "Interrupted during retry", e);
+        }
+    }
     ++counter;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbcae67 and 7ee65b1.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1 hunks)
  • server/src/main/java/org/opensearch/index/IndexSettings.java (1 hunks)
  • server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (10 hunks)
  • server/src/main/java/org/opensearch/index/mapper/MapperService.java (2 hunks)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java (0 hunks)
  • server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (4 hunks)
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java (1 hunks)
💤 Files with no reviewable changes (1)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: gradle-check
  • GitHub Check: assemble (21, ubuntu-24.04-arm)
  • GitHub Check: assemble (25, windows-latest)
  • GitHub Check: assemble (25, ubuntu-latest)
  • GitHub Check: assemble (25, ubuntu-24.04-arm)
  • GitHub Check: assemble (21, ubuntu-latest)
  • GitHub Check: assemble (21, windows-latest)
  • GitHub Check: Analyze (java)
  • GitHub Check: precommit (25, windows-latest)
  • GitHub Check: precommit (25, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, windows-2025, true)
  • GitHub Check: precommit (25, macos-15)
  • GitHub Check: precommit (21, ubuntu-latest)
  • GitHub Check: precommit (21, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, macos-15-intel)
  • GitHub Check: precommit (25, macos-15-intel)
  • GitHub Check: precommit (21, macos-15)
  • GitHub Check: precommit (25, ubuntu-latest)
  • GitHub Check: precommit (21, windows-latest)
  • GitHub Check: detect-breaking-change
🔇 Additional comments (9)
server/src/main/java/org/opensearch/index/IndexSettings.java (1)

499-506: LGTM! Increased retry limits for lock acquisition.

The changes to INDEX_MAX_RETRY_ON_LOOKUP_MAP_LOCK_ACQUISITION_EXCEPTION appropriately raise the default from 15 to 100 and the maximum from 100 to 500, providing better tolerance for lock contention scenarios during index writes with context-aware grouping criteria.

server/src/main/java/org/opensearch/index/mapper/MapperService.java (2)

87-87: LGTM!

The Collectors import is necessary for the stream operations added in getCompositeFieldTypes().


694-696: Verify that filtering to StarTree types only doesn't break existing functionality.

This change narrows the return value to only StarTreeMapper.StarTreeFieldType instances. Ensure that:

  1. Other composite field type implementations (if any) are correctly handled elsewhere
  2. The lookup sets (fieldsPartOfCompositeMappings, nestedFieldsPartOfCompositeMappings) built from all compositeMappedFieldTypes remain sufficient for other composite types
  3. External callers of getCompositeFieldTypes() and isCompositeIndexPresent() are not expecting all composite field types

To verify: Search the codebase for other CompositeMappedFieldType implementations, all call sites of getCompositeFieldTypes() and isCompositeIndexPresent(), and confirm how the filtered results are used downstream.

server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1)

730-730: LGTM: Retry logic correctly moved to lower layer.

The removal of the exception-driven retry loop simplifies the bulk action layer. Retry handling for lookup map lock acquisition is now managed within CompositeIndexWriter.computeIndexWriterIfAbsentForCriteria (with bounded tryAcquire loops and a configurable max retry parameter), which provides better encapsulation.

server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (3)

497-502: LGTM: Finally block correctly handles lock cleanup.

The finally block now uses assert for the isHeldByCurrentThread() check, which addresses the previous concern about UnsupportedOperationException when assertions are disabled. In production, the lock is unconditionally closed on error; in tests/debug builds, ownership is validated.


692-693: LGTM: Improved encapsulation in iteration patterns.

The refactoring to iterate directly over DisposableIndexWriter values (instead of extracting and operating on IndexWriter instances) improves encapsulation and reduces coupling. The consistent pattern across getFlushingBytes(), getPendingNumDocs(), getTragicException(), ramBytesUsed(), and rollback() enhances maintainability.

Also applies to: 703-704, 732-743, 759-772, 797-806


210-210: Verify that visibility relaxations don't expose internal state inappropriately.

The removal of final modifiers from CriteriaBasedIndexWriterLookup, CriteriaBasedWriterLock, and LiveIndexWriterDeletesMap, along with widening mapReadLock to package-private and making current non-final, enables mocking in tests.

While these changes support testing, ensure they don't inadvertently expose internal state or allow unintended subclassing or mutation by other classes in the package. Specifically, verify that:

  • mapReadLock (now package-private) is not accessed outside of test and core composite writer code
  • The inner classes cannot be extended by unintended classes in the same package
  • current (now non-final) is not reassigned outside controlled contexts
server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (2)

72-77: LGTM: Test calls updated consistently with new signature.

The calls to computeIndexWriterIfAbsentForCriteria correctly pass the new MAX_NUMBER_OF_RETRIES parameter, maintaining existing test coverage while adapting to the updated signature.

Also applies to: 141-146, 197-202


44-46: LGTM: New test provides good coverage of retry exhaustion.

The new testMaxRetryCountWhenWriteLockDuringIndexing test effectively validates the bounded retry behavior by:

  • Mocking lock acquisition to always fail (return null)
  • Asserting that LookupMapLockAcquisitionException is thrown after exhausting retries
  • Verifying via Mockito that tryAcquire() is called exactly MAX_NUMBER_OF_RETRIES times

This complements the existing integration test at line 179 and provides focused unit-level coverage of the retry mechanism.

Also applies to: 208-227

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
server/src/main/java/org/opensearch/index/mapper/MapperService.java (2)

694-696: Consider renaming method to reflect StarTree-specific filtering.

The method name getCompositeFieldTypes() suggests it returns all composite field types, but it now filters to return only StarTreeMapper.StarTreeFieldType instances. This could mislead callers who expect all composite types.

Consider renaming to getStarTreeFieldTypes() or adding a clarifying comment:

+    /**
+     * Returns only StarTree composite field types.
+     * For all composite field types, use getCompositeFieldTypesFromMapper().
+     */
     public Set<CompositeMappedFieldType> getCompositeFieldTypes() {
         return compositeMappedFieldTypes.stream()
             .filter(compositeMappedFieldType -> compositeMappedFieldType instanceof StarTreeMapper.StarTreeFieldType)
             .collect(Collectors.toSet());
     }

694-696: Consider caching the filtered result to avoid repeated stream operations.

The method creates a new stream, filters, and collects to a Set on every invocation. Since compositeMappedFieldTypes only changes during merge operations (line 552), the filtered result could be cached in a separate volatile field and updated alongside compositeMappedFieldTypes.

Example optimization:

 private volatile Set<CompositeMappedFieldType> compositeMappedFieldTypes;
+private volatile Set<CompositeMappedFieldType> starTreeFieldTypes;
 
 // In internalMerge() after line 552:
 this.compositeMappedFieldTypes = getCompositeFieldTypesFromMapper();
+this.starTreeFieldTypes = compositeMappedFieldTypes.stream()
+    .filter(type -> type instanceof StarTreeMapper.StarTreeFieldType)
+    .collect(Collectors.toSet());
 
 public Set<CompositeMappedFieldType> getCompositeFieldTypes() {
-    return compositeMappedFieldTypes.stream()
-        .filter(compositeMappedFieldType -> compositeMappedFieldType instanceof StarTreeMapper.StarTreeFieldType)
-        .collect(Collectors.toSet());
+    return starTreeFieldTypes;
 }
server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (1)

475-489: Consider adding exponential backoff between retry attempts.

The retry loop repeatedly calls tryAcquire() without delay, creating a busy-wait pattern. With a default max retry of 100 (configurable up to 500), this can waste significant CPU cycles under contention.

A past review comment suggested adding exponential backoff, but the current code doesn't implement it. Consider adding a small delay (e.g., Thread.sleep(Math.min(1L << counter, 100L))) between attempts, with appropriate InterruptedException handling.

 int counter = 0;
 while ((current == null || current.isClosed()) && counter < maxRetryOnLookupMapAcquisitionException) {
     // This function acquires a first read lock on a map which does not have any write lock present. Current keeps
     // on getting rotated during refresh, so there will be one current on which read lock can be obtained.
     // Validate that no write lock is applied on the map and the map is not closed. Idea here is write lock was
     // never applied on this map as write lock gets only during closing time. We are doing this instead of acquire,
     // because acquire can also apply a read lock in case refresh completed and map is closed.
     current = this.current.mapReadLock.tryAcquire();
     if (current != null && current.isClosed() == true) {
         current.mapReadLock.close();
         current = null;
     }

+    if (current == null && counter < maxRetryOnLookupMapAcquisitionException - 1) {
+        try {
+            Thread.sleep(Math.min(1L << counter, 100L));
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new LookupMapLockAcquisitionException(shardId, "Interrupted during retry", e);
+        }
+    }
     ++counter;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbcae67 and 7ee65b1.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1 hunks)
  • server/src/main/java/org/opensearch/index/IndexSettings.java (1 hunks)
  • server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (10 hunks)
  • server/src/main/java/org/opensearch/index/mapper/MapperService.java (2 hunks)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java (0 hunks)
  • server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (4 hunks)
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java (1 hunks)
💤 Files with no reviewable changes (1)
  • server/src/test/java/org/opensearch/action/bulk/TransportShardBulkActionTests.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • CHANGELOG.md
  • server/src/test/java/org/opensearch/index/engine/CriteriaBasedCompositeIndexWriterBaseTests.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: gradle-check
  • GitHub Check: assemble (21, ubuntu-24.04-arm)
  • GitHub Check: assemble (25, windows-latest)
  • GitHub Check: assemble (25, ubuntu-latest)
  • GitHub Check: assemble (25, ubuntu-24.04-arm)
  • GitHub Check: assemble (21, ubuntu-latest)
  • GitHub Check: assemble (21, windows-latest)
  • GitHub Check: Analyze (java)
  • GitHub Check: precommit (25, windows-latest)
  • GitHub Check: precommit (25, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, windows-2025, true)
  • GitHub Check: precommit (25, macos-15)
  • GitHub Check: precommit (21, ubuntu-latest)
  • GitHub Check: precommit (21, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, macos-15-intel)
  • GitHub Check: precommit (25, macos-15-intel)
  • GitHub Check: precommit (21, macos-15)
  • GitHub Check: precommit (25, ubuntu-latest)
  • GitHub Check: precommit (21, windows-latest)
  • GitHub Check: detect-breaking-change
🔇 Additional comments (9)
server/src/main/java/org/opensearch/index/IndexSettings.java (1)

499-506: LGTM! Increased retry limits for lock acquisition.

The changes to INDEX_MAX_RETRY_ON_LOOKUP_MAP_LOCK_ACQUISITION_EXCEPTION appropriately raise the default from 15 to 100 and the maximum from 100 to 500, providing better tolerance for lock contention scenarios during index writes with context-aware grouping criteria.

server/src/main/java/org/opensearch/index/mapper/MapperService.java (2)

87-87: LGTM!

The Collectors import is necessary for the stream operations added in getCompositeFieldTypes().


694-696: Verify that filtering to StarTree types only doesn't break existing functionality.

This change narrows the return value to only StarTreeMapper.StarTreeFieldType instances. Ensure that:

  1. Other composite field type implementations (if any) are correctly handled elsewhere
  2. The lookup sets (fieldsPartOfCompositeMappings, nestedFieldsPartOfCompositeMappings) built from all compositeMappedFieldTypes remain sufficient for other composite types
  3. External callers of getCompositeFieldTypes() and isCompositeIndexPresent() are not expecting all composite field types

To verify: Search the codebase for other CompositeMappedFieldType implementations, all call sites of getCompositeFieldTypes() and isCompositeIndexPresent(), and confirm how the filtered results are used downstream.

server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java (1)

730-730: LGTM: Retry logic correctly moved to lower layer.

The removal of the exception-driven retry loop simplifies the bulk action layer. Retry handling for lookup map lock acquisition is now managed within CompositeIndexWriter.computeIndexWriterIfAbsentForCriteria (with bounded tryAcquire loops and a configurable max retry parameter), which provides better encapsulation.

server/src/main/java/org/opensearch/index/engine/CompositeIndexWriter.java (3)

497-502: LGTM: Finally block correctly handles lock cleanup.

The finally block now uses assert for the isHeldByCurrentThread() check, which addresses the previous concern about UnsupportedOperationException when assertions are disabled. In production, the lock is unconditionally closed on error; in tests/debug builds, ownership is validated.


692-693: LGTM: Improved encapsulation in iteration patterns.

The refactoring to iterate directly over DisposableIndexWriter values (instead of extracting and operating on IndexWriter instances) improves encapsulation and reduces coupling. The consistent pattern across getFlushingBytes(), getPendingNumDocs(), getTragicException(), ramBytesUsed(), and rollback() enhances maintainability.

Also applies to: 703-704, 732-743, 759-772, 797-806


210-210: Verify that visibility relaxations don't expose internal state inappropriately.

The removal of final modifiers from CriteriaBasedIndexWriterLookup, CriteriaBasedWriterLock, and LiveIndexWriterDeletesMap, along with widening mapReadLock to package-private and making current non-final, enables mocking in tests.

While these changes support testing, ensure they don't inadvertently expose internal state or allow unintended subclassing or mutation by other classes in the package. Specifically, verify that:

  • mapReadLock (now package-private) is not accessed outside of test and core composite writer code
  • The inner classes cannot be extended by unintended classes in the same package
  • current (now non-final) is not reassigned outside controlled contexts
server/src/test/java/org/opensearch/index/engine/CompositeIndexWriterForAppendTests.java (2)

72-77: LGTM: Test calls updated consistently with new signature.

The calls to computeIndexWriterIfAbsentForCriteria correctly pass the new MAX_NUMBER_OF_RETRIES parameter, maintaining existing test coverage while adapting to the updated signature.

Also applies to: 141-146, 197-202


44-46: LGTM: New test provides good coverage of retry exhaustion.

The new testMaxRetryCountWhenWriteLockDuringIndexing test effectively validates the bounded retry behavior by:

  • Mocking lock acquisition to always fail (return null)
  • Asserting that LookupMapLockAcquisitionException is thrown after exhausting retries
  • Verifying via Mockito that tryAcquire() is called exactly MAX_NUMBER_OF_RETRIES times

This complements the existing integration test at line 179 and provides focused unit-level coverage of the retry mechanism.

Also applies to: 208-227

@github-actions
Copy link
Contributor

github-actions bot commented Dec 3, 2025

❌ Gradle check result for 7ee65b1: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@github-actions
Copy link
Contributor

github-actions bot commented Dec 3, 2025

✅ Gradle check result for 7ee65b1: SUCCESS

@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

❌ Patch coverage is 52.63158% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.21%. Comparing base (d47931e) to head (7ee65b1).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../opensearch/index/engine/CompositeIndexWriter.java 48.57% 9 Missing and 9 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #20145      +/-   ##
============================================
- Coverage     73.30%   73.21%   -0.09%     
- Complexity    71732    71749      +17     
============================================
  Files          5793     5793              
  Lines        328056   328045      -11     
  Branches      47245    47243       -2     
============================================
- Hits         240476   240193     -283     
- Misses        68264    68567     +303     
+ Partials      19316    19285      -31     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +483 to +486
if (current != null && current.isClosed() == true) {
current.mapReadLock.close();
current = null;
}
Copy link
Contributor

@Bukhtawar Bukhtawar Dec 4, 2025

Choose a reason for hiding this comment

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

Should we move this logic centrally in close(). Also didn't quite understand why the close operation done as a part of write lock acquisition doesn't handle this logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This handles the scenario where try acquire succeded in obtaining the lock on the current writer but the map itself rotated and the writer got closed. In that case, we close the old writer as we retry to obtain lock again on the current. As this ensures the lock is correctly released on the old writer before we try acquiring lock on new writer.

Comment on lines +803 to +805
for (DisposableIndexWriter disposableIndexWriter : liveIndexWriterDeletesMap.old.criteriaBasedIndexWriterMap.values()) {
if (disposableIndexWriter.getIndexWriter().isOpen() == true) {
disposableIndexWriter.getIndexWriter().rollback();
Copy link
Contributor

Choose a reason for hiding this comment

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

This might not be thread-safe for instance the index writer might be closed while we are doing a rollback?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants