diff --git a/src/main/java/de/thm/arsnova/config/PersistanceConfig.java b/src/main/java/de/thm/arsnova/config/PersistanceConfig.java index 13d31dc60d9450547019904e4e337ed2c1dc3977..6c81d4956c5c48ffd2986cfdeff2d187e3078668 100644 --- a/src/main/java/de/thm/arsnova/config/PersistanceConfig.java +++ b/src/main/java/de/thm/arsnova/config/PersistanceConfig.java @@ -19,6 +19,8 @@ import org.springframework.context.annotation.Profile; @Configuration @Profile("!test") public class PersistanceConfig { + private static final int MIGRATION_SOCKET_TIMEOUT = 30000; + @Value("${couchdb.name}") private String couchDbName; @Value("${couchdb.host}") private String couchDbHost; @Value("${couchdb.port}") private int couchDbPort; @@ -45,6 +47,11 @@ public class PersistanceConfig { return new StdCouchDbInstance(couchDbHttpClientFactory().getObject()); } + @Bean + public StdCouchDbInstance couchDbMigrationInstance() throws Exception { + return new StdCouchDbInstance(couchDbMigrationHttpClientFactory().getObject()); + } + @Bean public HttpClientFactoryBean couchDbHttpClientFactory() throws Exception { final HttpClientFactoryBean factory = new HttpClientFactoryBean(); @@ -58,6 +65,14 @@ public class PersistanceConfig { return factory; } + @Bean + public HttpClientFactoryBean couchDbMigrationHttpClientFactory() throws Exception { + final HttpClientFactoryBean factory = couchDbHttpClientFactory(); + factory.setSocketTimeout(MIGRATION_SOCKET_TIMEOUT); + + return factory; + } + @Bean public CouchDbObjectMapperFactory couchDbObjectMapperFactory() { return new CouchDbObjectMapperFactory(); diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/migrations/V2ToV3Migration.java b/src/main/java/de/thm/arsnova/persistance/couchdb/migrations/V2ToV3Migration.java index becca1e06c2d97b3e44111c57d722bec69a5493b..904ddcae5b6b4649116668742bd39fc46bd71b71 100644 --- a/src/main/java/de/thm/arsnova/persistance/couchdb/migrations/V2ToV3Migration.java +++ b/src/main/java/de/thm/arsnova/persistance/couchdb/migrations/V2ToV3Migration.java @@ -31,6 +31,7 @@ import de.thm.arsnova.persistance.ContentRepository; import de.thm.arsnova.persistance.RoomRepository; import de.thm.arsnova.persistance.UserRepository; import de.thm.arsnova.persistance.couchdb.support.MangoCouchDbConnector; +import org.ektorp.DbAccessException; import org.ektorp.DocumentNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -97,13 +98,17 @@ public class V2ToV3Migration implements Migration { public void migrate() { createV2Index(); migrator.setIgnoreRevision(true); - migrateUsers(); - migrateUnregisteredUsers(); - migrateRooms(); - migrateMotds(); - migrateComments(); - migrateContents(); - migrateAnswers(); + try { + migrateUsers(); + migrateUnregisteredUsers(); + migrateRooms(); + migrateMotds(); + migrateComments(); + migrateContents(); + migrateAnswers(); + } catch (InterruptedException e) { + throw new DbAccessException(e); + } migrator.setIgnoreRevision(false); } @@ -156,7 +161,18 @@ public class V2ToV3Migration implements Migration { fromConnector.createPartialJsonIndex(MOTDLIST_INDEX, fields, filterSelector); } - private void migrateUsers() { + private void waitForV2Index(final String name) throws InterruptedException { + for (int i = 0; i < 10; i++) { + if (fromConnector.initializeIndex(name)) { + return; + } + Thread.sleep(10000 * Math.round(1.0 + 0.5 * i)); + } + } + + private void migrateUsers() throws InterruptedException { + waitForV2Index(USER_INDEX); + waitForV2Index(LOGGEDIN_INDEX); Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "userdetails"); MangoCouchDbConnector.MangoQuery query = new MangoCouchDbConnector.MangoQuery(queryOptions); @@ -189,7 +205,9 @@ public class V2ToV3Migration implements Migration { } } - private void migrateUnregisteredUsers() { + private void migrateUnregisteredUsers() throws InterruptedException { + waitForV2Index(USER_INDEX); + waitForV2Index(LOGGEDIN_INDEX); /* Load registered usernames to exclude them later */ Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "userdetails"); @@ -232,7 +250,8 @@ public class V2ToV3Migration implements Migration { } } - private void migrateRooms() { + private void migrateRooms() throws InterruptedException { + waitForV2Index(SESSION_INDEX); Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "session"); MangoCouchDbConnector.MangoQuery query = new MangoCouchDbConnector.MangoQuery(queryOptions); @@ -262,7 +281,8 @@ public class V2ToV3Migration implements Migration { } } - private void migrateMotds() { + private void migrateMotds() throws InterruptedException { + waitForV2Index(MOTD_INDEX); Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "motd"); /* Exclude outdated MotDs */ @@ -300,7 +320,8 @@ public class V2ToV3Migration implements Migration { } } - private void migrateComments() { + private void migrateComments() throws InterruptedException { + waitForV2Index(FULL_INDEX_BY_TYPE); Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "interposed_question"); MangoCouchDbConnector.MangoQuery query = new MangoCouchDbConnector.MangoQuery(queryOptions); @@ -343,7 +364,8 @@ public class V2ToV3Migration implements Migration { } } - private void migrateContents() { + private void migrateContents() throws InterruptedException { + waitForV2Index(FULL_INDEX_BY_TYPE); Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "skill_question"); MangoCouchDbConnector.MangoQuery query = new MangoCouchDbConnector.MangoQuery(queryOptions); @@ -376,7 +398,8 @@ public class V2ToV3Migration implements Migration { } } - private void migrateAnswers() { + private void migrateAnswers() throws InterruptedException { + waitForV2Index(FULL_INDEX_BY_TYPE); Map<String, Object> queryOptions = new HashMap<>(); queryOptions.put("type", "skill_question_answer"); MangoCouchDbConnector.MangoQuery query = new MangoCouchDbConnector.MangoQuery(queryOptions); @@ -414,10 +437,11 @@ public class V2ToV3Migration implements Migration { } } - private HashSet<String> migrateMotdIds(final Set<String> oldIds) { + private HashSet<String> migrateMotdIds(final Set<String> oldIds) throws InterruptedException { if (oldIds.isEmpty()) { return new HashSet<>(); } + waitForV2Index(MOTD_INDEX); Map<String, Object> queryOptions = new HashMap<>(); Map<String, Set<String>> subQuery1 = new HashMap<>(); subQuery1.put("$in", oldIds); @@ -434,7 +458,8 @@ public class V2ToV3Migration implements Migration { return new HashSet<>(fromConnector.query(query, "_id", String.class)); } - private MotdList loadMotdList(final String username) { + private MotdList loadMotdList(final String username) throws InterruptedException { + waitForV2Index(MOTDLIST_INDEX); HashMap<String, Object> motdListQueryOptions = new HashMap<>(); motdListQueryOptions.put("type", "motdlist"); motdListQueryOptions.put("username", username); diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/support/MangoCouchDbConnector.java b/src/main/java/de/thm/arsnova/persistance/couchdb/support/MangoCouchDbConnector.java index 15239844b939d7e3d91c8decf76f9b1d640a4828..2155757b5686339509d9a02c498e93a2455bd3f8 100644 --- a/src/main/java/de/thm/arsnova/persistance/couchdb/support/MangoCouchDbConnector.java +++ b/src/main/java/de/thm/arsnova/persistance/couchdb/support/MangoCouchDbConnector.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.util.Converter; import de.thm.arsnova.entities.serialization.View; import org.ektorp.CouchDbInstance; import org.ektorp.DbAccessException; +import org.ektorp.http.HttpResponse; import org.ektorp.impl.ObjectMapperFactory; import org.ektorp.impl.StdCouchDbConnector; import org.slf4j.Logger; @@ -19,6 +20,7 @@ import org.slf4j.LoggerFactory; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -102,6 +104,7 @@ public class MangoCouchDbConnector extends StdCouchDbConnector { this.selector = selector; } + @JsonInclude(JsonInclude.Include.ALWAYS) @JsonView(View.Persistence.class) public Map<String, ?> getSelector() { return selector; @@ -271,4 +274,23 @@ public class MangoCouchDbConnector extends StdCouchDbConnector { public void createJsonIndex(final String name, final List<MangoQuery.Sort> fields) { createPartialJsonIndex(name, fields, null); } + + public boolean initializeIndex(final String name) { + MangoQuery query = new MangoQuery(Collections.EMPTY_MAP); + query.setIndexDocument(name); + query.setLimit(0); + try { + String queryString = objectMapper.writeValueAsString(query); + logger.debug("Using Mango API query to initialize CouchDB index: {}", queryString); + HttpResponse response = restTemplate.postUncached(dbURI.append("_find").toString(), queryString); + response.releaseConnection(); + } catch (JsonProcessingException e) { + throw new DbAccessException("Could not serialize Mango query."); + } catch (DbAccessException e) { + logger.debug("CouchDB index is not ready yet: {}", name, e); + return false; + } + + return true; + } }