Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HSEARCH-3725 Add a reproducer for Gson bug 764
- Loading branch information
Showing
2 changed files
with
124 additions
and
11 deletions.
There are no files selected for viewing
108 changes: 108 additions & 0 deletions
108
...ate/search/integrationtest/backend/elasticsearch/gson/ElasticsearchGsonConcurrencyIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* Hibernate Search, full-text search for your domain model | ||
* | ||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later | ||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. | ||
*/ | ||
package org.hibernate.search.integrationtest.backend.elasticsearch.gson; | ||
|
||
import org.hibernate.search.engine.backend.document.IndexFieldReference; | ||
import org.hibernate.search.engine.backend.document.IndexObjectFieldReference; | ||
import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaElement; | ||
import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaObjectField; | ||
import org.hibernate.search.integrationtest.backend.tck.testsupport.util.rule.SearchSetupHelper; | ||
import org.hibernate.search.util.impl.test.annotation.TestForIssue; | ||
|
||
import org.junit.Rule; | ||
import org.junit.Test; | ||
|
||
/** | ||
* Reproducer for HSEARCH-3725, | ||
* which is caused by a bug in Gson: https://github.com/google/gson/issues/764 | ||
* <p> | ||
* The bug happens very rarely, so we run the test multiple times, | ||
* but there's no guarantee we will actually reproduce it. | ||
* On my machine (8 CPU cores), I've seen the test run hundreds of times in a row | ||
* without the bug occurring even once. | ||
* But sometimes, if you're lucky, it will occur (I've seen it). | ||
* <p> | ||
* For the bug to occur, we need to: | ||
* <ul> | ||
* <li>Initialize multiple indexes in parallel</li> | ||
* <li>Serialize a mapping, or at least convert it to JsonElements, as part of index initialization</li> | ||
* </ul> | ||
* If we reach some state where two threads are initializing the same Gson TypeAdapter simultaneously, | ||
* there is a small chance FutureTypeAdapter#write(JsonWriter, Object) | ||
* will see a null adapter and throw an exception. | ||
*/ | ||
@TestForIssue(jiraKey = "HSEARCH-3725") | ||
public class ElasticsearchGsonConcurrencyIT { | ||
|
||
/* | ||
* Please keep these constants reasonably low so that routine builds don't take forever: | ||
* test duration will be proportional to REPRODUCER_ATTEMPTS*INDEX_COUNT_PER_ATTEMPT. | ||
* However, you will need to raise them in your local copy of the code | ||
* to have a high chance of reproducing the bug. | ||
*/ | ||
private static final int REPRODUCER_ATTEMPTS = 20; | ||
// This must be at least 2, but you don't need more than the number of CPU cores. | ||
private static final int INDEX_COUNT_PER_ATTEMPT = 4; | ||
|
||
private static final String INDEX_NAME_PREFIX = "IndexName_"; | ||
|
||
@Rule | ||
public SearchSetupHelper setupHelper = new SearchSetupHelper(); | ||
|
||
@Test | ||
public void repeatedlyStartMultipleIndexesSerializingWithGsonInParallel() { | ||
for ( int i = 0; i < REPRODUCER_ATTEMPTS; i++ ) { | ||
startMultipleIndexesSerializingWithGsonInParallel(); | ||
setupHelper.cleanUp(); | ||
} | ||
} | ||
|
||
private void startMultipleIndexesSerializingWithGsonInParallel() { | ||
SearchSetupHelper.SetupContext setupCtx = setupHelper.start(); | ||
|
||
for ( int i = 0; i < INDEX_COUNT_PER_ATTEMPT; i++ ) { | ||
setupCtx = setupCtx.withIndex( | ||
INDEX_NAME_PREFIX + i, | ||
ctx -> new IndexMapping( ctx.getSchemaElement() ) | ||
); | ||
} | ||
|
||
setupCtx.setup(); | ||
} | ||
|
||
private static class IndexMapping { | ||
final ObjectMapping child; | ||
|
||
IndexMapping(IndexSchemaElement root) { | ||
// Two levels of nested properties are necessary to reproduce the Gson bug causing HSEARCH-3725 | ||
child = new ObjectMapping( root, "child", 3 ); | ||
} | ||
} | ||
|
||
private static class ObjectMapping { | ||
final IndexObjectFieldReference self; | ||
final IndexFieldReference<String> text; | ||
final ObjectMapping child; | ||
|
||
ObjectMapping(IndexSchemaElement parent, String name, int depth) { | ||
IndexSchemaObjectField objectField = parent.objectField( name ); | ||
self = objectField.toReference(); | ||
text = objectField.field( | ||
"text", | ||
f -> f.asString() | ||
) | ||
.toReference(); | ||
if ( depth > 1 ) { | ||
child = new ObjectMapping( objectField, name, depth - 1 ); | ||
} | ||
else { | ||
child = null; | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters