From 94822b50af4098d990d63e27bb3906cfa9c0ec37 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Wed, 26 Jul 2023 12:31:43 +0200 Subject: [PATCH 1/5] Change the database mode to READ_ONLY during the indexing by discovery consumer (IndexEventConsumer) --- .../main/java/org/dspace/discovery/IndexEventConsumer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java index 4ff1f31344..d670f5b339 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java @@ -201,6 +201,10 @@ public class IndexEventConsumer implements Consumer { @Override public void end(Context ctx) throws Exception { + // Change the mode to readonly to improve the performance + Context.Mode originalMode = ctx.getCurrentMode(); + ctx.setMode(Context.Mode.READ_ONLY); + try { for (String uid : uniqueIdsToDelete) { try { @@ -231,6 +235,8 @@ public class IndexEventConsumer implements Consumer { createdItemsToUpdate.clear(); } } + + ctx.setMode(originalMode); } private void indexObject(Context ctx, IndexableObject iu, boolean preDb) throws SQLException { From c33d3fa87d6c29533d379939bd23b29ff3d9b5c9 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 28 Jul 2023 09:19:37 +0200 Subject: [PATCH 2/5] Add functions to do a manual flush of the db session and call flush before change to READ_ONLY mode to be sure we index the current object --- .../src/main/java/org/dspace/core/Context.java | 10 ++++++++++ .../src/main/java/org/dspace/core/DBConnection.java | 8 ++++++++ .../java/org/dspace/core/HibernateDBConnection.java | 13 +++++++++++++ .../org/dspace/discovery/IndexEventConsumer.java | 10 +++++++--- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 82b39dd2df..6382e72430 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -880,6 +880,16 @@ public class Context implements AutoCloseable { dbConnection.uncacheEntity(entity); } + /** + * Flush the current Session to synchronizes the in-memory state of the Session + * with the database (write changes to the database) + * + * @throws SQLException passed through. + */ + public void flushDBChanges() throws SQLException { + dbConnection.flushSession(); + } + public Boolean getCachedAuthorizationResult(DSpaceObject dspaceObject, int action, EPerson eperson) { if (isReadOnly()) { return readOnlyCache.getCachedAuthorizationResult(dspaceObject, action, eperson); diff --git a/dspace-api/src/main/java/org/dspace/core/DBConnection.java b/dspace-api/src/main/java/org/dspace/core/DBConnection.java index cb5825eec1..66e4a65dbf 100644 --- a/dspace-api/src/main/java/org/dspace/core/DBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/DBConnection.java @@ -148,4 +148,12 @@ public interface DBConnection { * @throws java.sql.SQLException passed through. */ public void uncacheEntity(E entity) throws SQLException; + + /** + * Do a manual flush. This synchronizes the in-memory state of the Session + * with the database (write changes to the database) + * + * @throws SQLException passed through. + */ + public void flushSession() throws SQLException; } diff --git a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java index 3321e4d837..b371af80ee 100644 --- a/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java +++ b/dspace-api/src/main/java/org/dspace/core/HibernateDBConnection.java @@ -337,4 +337,17 @@ public class HibernateDBConnection implements DBConnection { } } } + + /** + * Do a manual flush. This synchronizes the in-memory state of the Session + * with the database (write changes to the database) + * + * @throws SQLException passed through. + */ + @Override + public void flushSession() throws SQLException { + if (getSession().isDirty()) { + getSession().flush(); + } + } } diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java index d670f5b339..fd255e9ffc 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java @@ -201,7 +201,11 @@ public class IndexEventConsumer implements Consumer { @Override public void end(Context ctx) throws Exception { - // Change the mode to readonly to improve the performance + // Change the mode to readonly to improve performance + // First, we flush the changes to database, if session is dirty, has pending changes + // to synchronize with database, without this flush it could index an old version of + // the object + ctx.flushDBChanges(); Context.Mode originalMode = ctx.getCurrentMode(); ctx.setMode(Context.Mode.READ_ONLY); @@ -234,9 +238,9 @@ public class IndexEventConsumer implements Consumer { uniqueIdsToDelete.clear(); createdItemsToUpdate.clear(); } - } - ctx.setMode(originalMode); + ctx.setMode(originalMode); + } } private void indexObject(Context ctx, IndexableObject iu, boolean preDb) throws SQLException { From 00a65312ccb52481cd72653b4c5465b7d16c760e Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 13 Oct 2023 20:52:08 +0200 Subject: [PATCH 3/5] Flush database changes after switching to READONLY mode --- .../main/java/org/dspace/core/Context.java | 19 +++++++++---------- .../dspace/discovery/IndexEventConsumer.java | 4 ---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/core/Context.java b/dspace-api/src/main/java/org/dspace/core/Context.java index 6382e72430..09b9c4a32d 100644 --- a/dspace-api/src/main/java/org/dspace/core/Context.java +++ b/dspace-api/src/main/java/org/dspace/core/Context.java @@ -810,6 +810,15 @@ public class Context implements AutoCloseable { readOnlyCache.clear(); } + // When going to READ_ONLY, flush database changes to ensure that the current data is retrieved + if (newMode == Mode.READ_ONLY && mode != Mode.READ_ONLY) { + try { + dbConnection.flushSession(); + } catch (SQLException ex) { + log.warn("Unable to flush database changes after switching to READ_ONLY mode", ex); + } + } + //save the new mode mode = newMode; } @@ -880,16 +889,6 @@ public class Context implements AutoCloseable { dbConnection.uncacheEntity(entity); } - /** - * Flush the current Session to synchronizes the in-memory state of the Session - * with the database (write changes to the database) - * - * @throws SQLException passed through. - */ - public void flushDBChanges() throws SQLException { - dbConnection.flushSession(); - } - public Boolean getCachedAuthorizationResult(DSpaceObject dspaceObject, int action, EPerson eperson) { if (isReadOnly()) { return readOnlyCache.getCachedAuthorizationResult(dspaceObject, action, eperson); diff --git a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java index fd255e9ffc..847e235fa9 100644 --- a/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java +++ b/dspace-api/src/main/java/org/dspace/discovery/IndexEventConsumer.java @@ -202,10 +202,6 @@ public class IndexEventConsumer implements Consumer { public void end(Context ctx) throws Exception { // Change the mode to readonly to improve performance - // First, we flush the changes to database, if session is dirty, has pending changes - // to synchronize with database, without this flush it could index an old version of - // the object - ctx.flushDBChanges(); Context.Mode originalMode = ctx.getCurrentMode(); ctx.setMode(Context.Mode.READ_ONLY); From d19a9599b5f08a567c93d2e167e219673518fb78 Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 13 Oct 2023 20:59:33 +0200 Subject: [PATCH 4/5] Add test to check retrieving of policies after changing mode to READ_ONLY --- .../java/org/dspace/core/ContextModeIT.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 dspace-api/src/test/java/org/dspace/core/ContextModeIT.java diff --git a/dspace-api/src/test/java/org/dspace/core/ContextModeIT.java b/dspace-api/src/test/java/org/dspace/core/ContextModeIT.java new file mode 100644 index 0000000000..f689551f1a --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/core/ContextModeIT.java @@ -0,0 +1,42 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.core; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.factory.AuthorizeServiceFactory; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.builder.CommunityBuilder; +import org.junit.Test; + +public class ContextModeIT extends AbstractIntegrationTestWithDatabase { + + AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); + + @Test + public void testGetPoliciesNewCommunityAfterReadOnlyModeChange() throws Exception { + + context.turnOffAuthorisationSystem(); + parentCommunity = CommunityBuilder.createCommunity(context) + .withName("Parent Community") + .build(); + context.restoreAuthSystemState(); + + context.setMode(Context.Mode.READ_ONLY); + + List policies = authorizeService.getPoliciesActionFilter(context, parentCommunity, + Constants.READ); + + assertEquals("Should return the default anonymous group read policy", 1, policies.size()); + } + +} From a5567992bbe456cd33c68f695a2364f507149e7a Mon Sep 17 00:00:00 2001 From: Toni Prieto Date: Fri, 27 Oct 2023 09:11:12 +0200 Subject: [PATCH 5/5] Change class name to ContextIT and correct a test --- .../org/dspace/core/{ContextModeIT.java => ContextIT.java} | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) rename dspace-api/src/test/java/org/dspace/core/{ContextModeIT.java => ContextIT.java} (83%) diff --git a/dspace-api/src/test/java/org/dspace/core/ContextModeIT.java b/dspace-api/src/test/java/org/dspace/core/ContextIT.java similarity index 83% rename from dspace-api/src/test/java/org/dspace/core/ContextModeIT.java rename to dspace-api/src/test/java/org/dspace/core/ContextIT.java index f689551f1a..6cf8336171 100644 --- a/dspace-api/src/test/java/org/dspace/core/ContextModeIT.java +++ b/dspace-api/src/test/java/org/dspace/core/ContextIT.java @@ -18,7 +18,7 @@ import org.dspace.authorize.service.AuthorizeService; import org.dspace.builder.CommunityBuilder; import org.junit.Test; -public class ContextModeIT extends AbstractIntegrationTestWithDatabase { +public class ContextIT extends AbstractIntegrationTestWithDatabase { AuthorizeService authorizeService = AuthorizeServiceFactory.getInstance().getAuthorizeService(); @@ -26,6 +26,11 @@ public class ContextModeIT extends AbstractIntegrationTestWithDatabase { public void testGetPoliciesNewCommunityAfterReadOnlyModeChange() throws Exception { context.turnOffAuthorisationSystem(); + + // First disable the index consumer. The indexing process calls the authorizeService + // function used in this test and may affect the test + context.setDispatcher("noindex"); + parentCommunity = CommunityBuilder.createCommunity(context) .withName("Parent Community") .build();