Skip to content

Commit

Permalink
avoiding concurrent modification execptions and preserving the metada…
Browse files Browse the repository at this point in the history
…ta while rebuilding indexes #7125
  • Loading branch information
taburet committed Feb 7, 2017
1 parent 63985a8 commit 2209722
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 81 deletions.
Expand Up @@ -750,6 +750,8 @@ public ODocument checkEntry(final OIdentifiable iRecord, final Object iKey) {

public ODocument updateConfiguration() {
configuration.updateConfiguration(type, name, version, indexDefinition, clustersToIndex, algorithm, valueContainerAlgorithm);
if (metadata != null)
configuration.document.field(OIndexInternal.METADATA, metadata, OType.EMBEDDED);
return configuration.getDocument();
}

Expand Down
Expand Up @@ -20,6 +20,7 @@
package com.orientechnologies.orient.core.index;

import com.orientechnologies.common.concur.resource.OCloseable;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.util.OMultiKey;
import com.orientechnologies.orient.core.config.OStorageConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal;
Expand Down Expand Up @@ -108,17 +109,23 @@ public Object call() throws Exception {
acquireExclusiveLock();

try {
boolean saved = false;
for (int retry = 0; retry < 10; retry++)
try {

toStream();
document.save();
saved = true;
break;

} catch (OConcurrentModificationException e) {
OLogManager.instance().debug(this, "concurrent modification while saving index manager configuration", e);
reload(null, true);
}

if (!saved)
OLogManager.instance().error(this, "failed to save the index manager configuration after 10 retries");

return null;

} finally {
Expand Down
Expand Up @@ -40,7 +40,6 @@
import com.orientechnologies.orient.core.metadata.security.OSecurityNull;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.OStorageProxy;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

Expand All @@ -54,10 +53,10 @@
*/
@SuppressFBWarnings("EQ_DOESNT_OVERRIDE_EQUALS")
public class OIndexManagerShared extends OIndexManagerAbstract {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;

protected volatile transient Thread recreateIndexesThread = null;
private volatile boolean rebuildCompleted = false;
protected volatile transient Thread recreateIndexesThread = null;
private volatile boolean rebuildCompleted = false;

public OIndexManagerShared(final ODatabaseDocument iDatabase) {
super(iDatabase);
Expand All @@ -76,18 +75,13 @@ public OIndex<?> getIndexInternal(final String name) {
/**
* Create a new index with default algorithm.
*
* @param iName
* - name of index
* @param iType
* - index type. Specified by plugged index factories.
* @param indexDefinition
* metadata that describes index structure
* @param clusterIdsToIndex
* ids of clusters that index should track for changes.
* @param progressListener
* listener to track task progress.
* @param metadata
* document with additional properties that can be used by index engine.
* @param iName name of index
* @param iType index type. Specified by plugged index factories.
* @param indexDefinition metadata that describes index structure
* @param clusterIdsToIndex ids of clusters that index should track for changes.
* @param progressListener listener to track task progress.
* @param metadata document with additional properties that can be used by index engine.
*
* @return a newly created index instance
*/
public OIndex<?> createIndex(final String iName, final String iType, final OIndexDefinition indexDefinition,
Expand All @@ -100,20 +94,14 @@ public OIndex<?> createIndex(final String iName, final String iType, final OInde
* <p>
* May require quite a long time if big amount of data should be indexed.
*
* @param iName
* name of index
* @param type
* index type. Specified by plugged index factories.
* @param indexDefinition
* metadata that describes index structure
* @param clusterIdsToIndex
* ids of clusters that index should track for changes.
* @param progressListener
* listener to track task progress.
* @param metadata
* document with additional properties that can be used by index engine.
* @param algorithm
* tip to an index factory what algorithm to use
* @param iName name of index
* @param type index type. Specified by plugged index factories.
* @param indexDefinition metadata that describes index structure
* @param clusterIdsToIndex ids of clusters that index should track for changes.
* @param progressListener listener to track task progress.
* @param metadata document with additional properties that can be used by index engine.
* @param algorithm tip to an index factory what algorithm to use
*
* @return a newly created index instance
*/
public OIndex<?> createIndex(final String iName, String type, final OIndexDefinition indexDefinition,
Expand Down Expand Up @@ -173,8 +161,8 @@ public OIndex<?> createIndex(final String iName, String type, final OIndexDefini
}

// decide which cluster to use ("index" - for automatic and "manindex" for manual)
final String clusterName = indexDefinition != null && indexDefinition.getClassName() != null ? defaultClusterName
: manualClusterName;
final String clusterName =
indexDefinition != null && indexDefinition.getClassName() != null ? defaultClusterName : manualClusterName;

index.create(iName, indexDefinition, clusterName, clustersToIndex, true, progressListener);

Expand Down Expand Up @@ -308,22 +296,18 @@ public ODocument toStream() {

@Override
public void recreateIndexes() {
final ODatabaseDocumentInternal db;

acquireExclusiveLock();
try {
if (recreateIndexesThread != null && recreateIndexesThread.isAlive())
// BUILDING ALREADY IN PROGRESS
return;

final ODatabaseDocument db = getDatabase();
document = db.load(new ORecordId(getDatabase().getStorage().getConfiguration().indexMgrRecordId));
final ODocument doc = new ODocument();
document.copyTo(doc);

// USE A NEW DB INSTANCE
final ODatabaseDocumentInternal newDb = new ODatabaseDocumentTx(db.getURL());

Runnable recreateIndexesTask = new RecreateIndexesTask(newDb, doc);
db = getDatabase();
document = db.load(new ORecordId(db.getStorage().getConfiguration().indexMgrRecordId));

final Runnable recreateIndexesTask = new RecreateIndexesTask(db.getURL());
recreateIndexesThread = new Thread(recreateIndexesTask, "OrientDB rebuild indexes");
recreateIndexesThread.start();
} finally {
Expand All @@ -332,8 +316,7 @@ public void recreateIndexes() {

if (OGlobalConfiguration.INDEX_SYNCHRONOUS_AUTO_REBUILD.getValueAsBoolean()) {
waitTillIndexRestore();

getDatabase().getMetadata().reload();
db.getMetadata().reload();
}
}

Expand Down Expand Up @@ -385,16 +368,16 @@ protected void fromStream() {
while (indexConfigurationIterator.hasNext()) {
final ODocument d = indexConfigurationIterator.next();
try {
final int indexVersion = d.field(OIndexInternal.INDEX_VERSION) == null ? 1
: (Integer) d.field(OIndexInternal.INDEX_VERSION);
final int indexVersion =
d.field(OIndexInternal.INDEX_VERSION) == null ? 1 : (Integer) d.field(OIndexInternal.INDEX_VERSION);

final OIndexMetadata newIndexMetadata = OIndexAbstract.loadMetadataInternal(d,
(String) d.field(OIndexInternal.CONFIG_TYPE), (String) d.field(OIndexInternal.ALGORITHM),
d.<String> field(OIndexInternal.VALUE_CONTAINER_ALGORITHM));
final OIndexMetadata newIndexMetadata = OIndexAbstract
.loadMetadataInternal(d, (String) d.field(OIndexInternal.CONFIG_TYPE), (String) d.field(OIndexInternal.ALGORITHM),
d.<String>field(OIndexInternal.VALUE_CONTAINER_ALGORITHM));

index = OIndexes.createIndex(getDatabase(), newIndexMetadata.getName(), newIndexMetadata.getType(),
newIndexMetadata.getAlgorithm(), newIndexMetadata.getValueContainerAlgorithm(),
(ODocument) d.field(OIndexInternal.METADATA), indexVersion);
index = OIndexes
.createIndex(getDatabase(), newIndexMetadata.getName(), newIndexMetadata.getType(), newIndexMetadata.getAlgorithm(),
newIndexMetadata.getValueContainerAlgorithm(), (ODocument) d.field(OIndexInternal.METADATA), indexVersion);

final String normalizedName = newIndexMetadata.getName().toLowerCase(locale);

Expand Down Expand Up @@ -494,23 +477,22 @@ public void removeClassPropertyIndex(final OIndex<?> idx) {
}

private class RecreateIndexesTask implements Runnable {
private final ODatabaseDocumentInternal newDb;
private final ODocument doc;
private int ok;
private int errors;

public RecreateIndexesTask(ODatabaseDocumentInternal newDb, ODocument doc) {
this.newDb = newDb;
this.doc = doc;
private ODatabaseDocumentInternal newDb;
private Collection<ODocument> indexesToRebuild;

private final String url;
private int ok;
private int errors;

public RecreateIndexesTask(String url) {
this.url = url;
}

@Override
public void run() {
try {
setUpDatabase();

final Collection<ODocument> idxs = getConfiguration();

final OStorage storage = newDb.getStorage().getUnderlying();

if (storage instanceof OAbstractPaginatedStorage) {
Expand All @@ -519,7 +501,7 @@ public void run() {
}

try {
recreateIndexes(idxs);
recreateIndexes();
} finally {
if (storage instanceof OAbstractPaginatedStorage) {
final OAbstractPaginatedStorage abstractPaginatedStorage = (OAbstractPaginatedStorage) storage;
Expand All @@ -533,10 +515,10 @@ public void run() {
}
}

private void recreateIndexes(Collection<ODocument> idxs) {
private void recreateIndexes() {
ok = 0;
errors = 0;
for (ODocument idx : idxs) {
for (ODocument idx : indexesToRebuild) {
try {
recreateIndex(idx);

Expand All @@ -546,7 +528,7 @@ private void recreateIndexes(Collection<ODocument> idxs) {
}
}

save();
newDb.getMetadata().getIndexManager().save();

rebuildCompleted = true;

Expand All @@ -563,15 +545,16 @@ private void recreateIndex(ODocument idx) {
index.loadFromConfiguration(idx);
index.delete();
} catch (Exception e) {
OLogManager.instance().error(this, "Error on removing index '%s' on rebuilding. Trying removing index files (Cause: %s)",
index.getName(), e);
OLogManager.instance()
.error(this, "Error on removing index '%s' on rebuilding. Trying removing index files (Cause: %s)", index.getName(),
e);

// TRY DELETING ALL THE FILES RELATIVE TO THE INDEX
for (Iterator<OIndexFactory> it = OIndexes.getAllFactories(); it.hasNext();) {
for (Iterator<OIndexFactory> it = OIndexes.getAllFactories(); it.hasNext(); ) {
try {
final OIndexFactory indexFactory = it.next();
final OIndexEngine engine = indexFactory.createIndexEngine(null, index.getName(), false, getDatabase().getStorage(),
0, null);
final OIndexEngine engine = indexFactory
.createIndexEngine(null, index.getName(), false, getDatabase().getStorage(), 0, null);

engine.deleteWithoutLoad(index.getName());
} catch (Exception e2) {
Expand Down Expand Up @@ -645,22 +628,27 @@ private OIndexInternal<?> createIndex(ODocument idx) {
return OIndexes.createIndex(newDb, indexName, indexType, algorithm, valueContainerAlgorithm, metadata, -1);
}

private Collection<ODocument> getConfiguration() {
final Collection<ODocument> idxs = doc.field(CONFIG_INDEXES);
if (idxs == null) {
OLogManager.instance().warn(this, "List of indexes is empty");
return Collections.emptyList();
}
return idxs;
}

private void setUpDatabase() {
newDb = new ODatabaseDocumentTx(url);
newDb.activateOnCurrentThread();
newDb.resetInitialization();
newDb.setProperty(ODatabase.OPTIONS.SECURITY.toString(), OSecurityNull.class);
newDb.open("admin", "nopass");

ODatabaseRecordThreadLocal.INSTANCE.set(newDb);
acquireExclusiveLock();
try {
final Collection<ODocument> knownIndexes = document.field(CONFIG_INDEXES);
if (knownIndexes == null) {
OLogManager.instance().warn(this, "List of indexes is empty");
indexesToRebuild = Collections.emptyList();
} else {
indexesToRebuild = new ArrayList<ODocument>();
for (ODocument index : knownIndexes)
indexesToRebuild.add(index.copy()); // make copies to safely iterate them later
}
} finally {
releaseExclusiveLock();
}
}
}

Expand Down
Expand Up @@ -182,6 +182,8 @@ public void testEntriesAddition() throws Exception {
assertThat(index.getMetadata().<String>field("default")).isNotNull();
assertThat(index.getMetadata().<String>field("default"))
.isEqualTo("org.apache.lucene.analysis.core.KeywordAnalyzer");
assertThat(index.getMetadata().<String>field("unknownKey"))
.isEqualTo("unknownValue");

//sometimes it is not null, and all works fine
res = testDocumentTx.query(new OSQLSynchQuery<ODocument>("select from Person where name lucene 'Rob*' "));
Expand All @@ -206,7 +208,7 @@ private void createSchema(ODatabaseDocumentTx db) {
db.command(new OCommandSQL("Create class Person")).execute();
db.command(new OCommandSQL("Create property Person.name STRING")).execute();
db.command(new OCommandSQL(
"Create index Person.name on Person(name) fulltext engine lucene metadata {'default':'org.apache.lucene.analysis.core.KeywordAnalyzer'} "))
"Create index Person.name on Person(name) fulltext engine lucene metadata {'default':'org.apache.lucene.analysis.core.KeywordAnalyzer', 'unknownKey':'unknownValue'}"))
.execute();
db.getMetadata().getIndexManager().reload();

Expand Down

0 comments on commit 2209722

Please sign in to comment.