diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
index f9f23f8f157135f0ed49317af089372c0c5d0137..4fab86835d92c2da74b87c8fdd37c7f8fc9f29fc 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java
@@ -1620,12 +1620,14 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements
 
         // in the first pass register samples without container to avoid dependency inversion
         BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable,
-                containerSamples, registratorOrNull), progress, "createContainerSamples");
+                containerSamples, registratorOrNull), getBatchSize(operationDetails), progress,
+                "createContainerSamples");
 
         // register samples with a container identifier
         // (container should have been created in the first pass)
         BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable,
-                containedSamples, registratorOrNull), progress, "createContainedSamples");
+                containedSamples, registratorOrNull), getBatchSize(operationDetails), progress,
+                "createContainedSamples");
 
         return newSamples.size();
     }
@@ -1662,7 +1664,7 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements
         final ISampleTable sampleTable = businessObjectFactory.createSampleTable(session);
         sampleTable.checkBeforeUpdate(sampleUpdates);
         BatchOperationExecutor.executeInBatches(new SampleUpdate(sampleTable, sampleUpdates),
-                progress, "updateSamples");
+                getBatchSize(operationDetails), progress, "updateSamples");
         return sampleUpdateCount;
     }
 
@@ -1751,7 +1753,8 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements
         final IDataSetTable dataSetTable = businessObjectFactory.createDataSetTable(session);
         dataSetTable.checkBeforeUpdate(dataSetUpdates);
         BatchOperationExecutor.executeInBatches(
-                new DataSetBatchUpdate(dataSetTable, dataSetUpdates), progress, "updateDataSets");
+                new DataSetBatchUpdate(dataSetTable, dataSetUpdates),
+                getBatchSize(operationDetails), progress, "updateDataSets");
 
         return dataSetUpdatesCount;
     }
@@ -2057,4 +2060,11 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements
             return null;
         }
     }
+
+    private int getBatchSize(AtomicEntityOperationDetails details)
+    {
+        return details == null || details.getBatchSizeOrNull() == null ? BatchOperationExecutor
+                .getDefaultBatchSize() : details.getBatchSizeOrNull();
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java
index cc36cabd0bfa58a5f37c4bac3f5ccb5b6f550ef3..ca94d6ef787e6a23ad3ccfee2d510f1f6172aefe 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/batch/BatchOperationExecutor.java
@@ -84,6 +84,11 @@ public class BatchOperationExecutor
         }
     }
 
+    public static int getDefaultBatchSize()
+    {
+        return DEFAULT_BATCH_SIZE;
+    }
+
     private static void notifyProgressListener(IProgressListener progressListenerOrNull,
             String progressPhaseOrNull, int maxIndex, int currentIndex)
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java
index d85b3b4a0627063b762ebb06a1c7cc2e99dddb7a..e75942fd576a61a14dd051a6d59a66df2caca400 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/AtomicEntityOperationDetails.java
@@ -68,6 +68,8 @@ public class AtomicEntityOperationDetails implements Serializable
 
     private final List<DataSetBatchUpdatesDTO> dataSetUpdates;
 
+    private Integer batchSizeOrNull;
+
     public AtomicEntityOperationDetails(TechId registrationId, String userIdOrNull,
             List<NewSpace> spaceRegistrations, List<NewProject> projectRegistrations,
             List<NewExperiment> experimentRegistrations, List<SampleUpdatesDTO> sampleUpdates,
@@ -89,6 +91,20 @@ public class AtomicEntityOperationDetails implements Serializable
         this.dataSetUpdates = new ArrayList<DataSetBatchUpdatesDTO>(dataSetUpdates);
     }
 
+    public AtomicEntityOperationDetails(TechId registrationId, String userIdOrNull,
+            List<NewSpace> spaceRegistrations, List<NewProject> projectRegistrations,
+            List<NewExperiment> experimentRegistrations, List<SampleUpdatesDTO> sampleUpdates,
+            List<NewSample> sampleRegistrations,
+            Map<String, List<NewMaterial>> materialRegistrations,
+            List<? extends NewExternalData> dataSetRegistrations,
+            List<DataSetBatchUpdatesDTO> dataSetUpdates, Integer batchSizeOrNull)
+    {
+        this(registrationId, userIdOrNull, spaceRegistrations, projectRegistrations,
+                experimentRegistrations, sampleUpdates, sampleRegistrations, materialRegistrations,
+                dataSetRegistrations, dataSetUpdates);
+        this.batchSizeOrNull = batchSizeOrNull;
+    }
+
     public TechId getRegistrationIdOrNull()
     {
         return registrationIdOrNull;
@@ -144,6 +160,11 @@ public class AtomicEntityOperationDetails implements Serializable
         return materialRegistrations;
     }
 
+    public Integer getBatchSizeOrNull()
+    {
+        return batchSizeOrNull;
+    }
+
     @Override
     public String toString()
     {
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java
index 3641ffc386180c22aac9f3d07e39a1c042f64118..ca7987900ab637dc9a3557359f70b816547ebeea 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceDatabaseTest.java
@@ -35,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.AbstractDAOTest;
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
@@ -45,6 +46,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentFetchOption;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentFetchOptions;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityPropertiesHolder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.NewAttachment;
@@ -140,53 +142,56 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest
     @Test
     public void testPerformEntityOperationsUpdateSample()
     {
-        // Find the samples to add
-        SearchCriteria searchCriteria = new SearchCriteria();
-        searchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch(
-                MatchClauseAttribute.CODE, "3VCP7"));
-        List<Sample> samplesToUpdate = service.searchForSamples(sessionToken, searchCriteria);
-        assertEquals(1, samplesToUpdate.size());
-        Sample sampleToUpdate = samplesToUpdate.get(0);
+        Sample sample = findSampleByCode("3VCP7");
 
         // Update the comment
-        String newComment = "This is a new comment. This is not the old comment.";
-        String oldComment = EntityHelper.tryFindPropertyValue(sampleToUpdate, "COMMENT");
-        assertFalse(newComment.equals(oldComment));
-        EntityHelper.createOrUpdateProperty(sampleToUpdate, "COMMENT", newComment);
+        String sampleComment = "This is a new comment for a sample.";
+        updateEntityProperty(sample, "COMMENT", sampleComment);
 
         // Update the parents
-        SearchCriteria parentSearchCriteria = new SearchCriteria();
-        parentSearchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch(
-                MatchClauseAttribute.CODE, "3V-126"));
-        List<Sample> parentsToAdd = service.searchForSamples(sessionToken, parentSearchCriteria);
-        assertEquals(1, parentsToAdd.size());
-        Sample parentToAdd = parentsToAdd.get(0);
-        assertEquals(1, sampleToUpdate.getParents().size());
-        sampleToUpdate.addParent(parentToAdd);
+        Sample parent = findSampleByCode("3V-126");
+        String parentComment = "This is a new comment for a parent.";
+        updateEntityProperty(parent, "COMMENT", parentComment);
+        assertEquals(1, sample.getParents().size());
+        sample.addParent(parent);
 
-        performSampleUpdate(sampleToUpdate);
+        SampleUpdatesDTO parentUpdate = convertToSampleUpdateDTO(parent);
+        SampleUpdatesDTO sampleUpdate = convertToSampleUpdateDTO(sample);
+        performSampleUpdate(Arrays.asList(sampleUpdate, parentUpdate), 1);
 
         // Now retrieve the sample again and check that the properties were updated.
-        List<Sample> updatedSamples = service.searchForSamples(sessionToken, searchCriteria);
-        assertEquals(1, updatedSamples.size());
-        Sample updatedSample = updatedSamples.get(0);
+        Sample updatedSample = findSampleByCode("3VCP7");
+        Sample updatedParent = findSampleByCode("3V-126");
+
         assertTrue("The modification date should have been updated", updatedSample
-                .getModificationDate().compareTo(sampleToUpdate.getModificationDate()) > 0);
-        assertEquals(newComment, EntityHelper.tryFindPropertyValue(updatedSample, "COMMENT"));
+                .getModificationDate().compareTo(sample.getModificationDate()) > 0);
+        assertTrue("The modification date should have been updated", updatedParent
+                .getModificationDate().compareTo(sample.getModificationDate()) > 0);
+        assertEquals(sampleComment, EntityHelper.tryFindPropertyValue(updatedSample, "COMMENT"));
+        assertEquals(parentComment, EntityHelper.tryFindPropertyValue(updatedParent, "COMMENT"));
         assertEquals(2, updatedSample.getParents().size());
     }
 
+    @Test(expectedExceptions = EnvironmentFailureException.class)
+    public void testPerformEntityOperationsUpdateStaleSample()
+    {
+        Sample sampleToUpdate = findSampleByCode("3VCP7");
+
+        // Update the comment
+        String newComment = "This is a new comment. This is not the old comment.";
+        updateEntityProperty(sampleToUpdate, "COMMENT", newComment);
+
+        SampleUpdatesDTO sampleUpdate = convertToSampleUpdateDTO(sampleToUpdate);
+        sampleUpdate.setVersion(new Date());
+        performSampleUpdate(Arrays.asList(sampleUpdate), 1);
+    }
+
     @SuppressWarnings("null")
     @Test
     public void testPerformEntityOperationsCreateSample()
     {
         // Get the parents
-        SearchCriteria parentSearchCriteria = new SearchCriteria();
-        parentSearchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch(
-                MatchClauseAttribute.CODE, "3VCP7"));
-        List<Sample> parentsToAdd = service.searchForSamples(sessionToken, parentSearchCriteria);
-        assertEquals(1, parentsToAdd.size());
-        Sample parent = parentsToAdd.get(0);
+        Sample parent = findSampleByCode("3VCP7");
 
         NewSample sampleToCreate = new NewSample();
         String newSampleIdentifier = "/" + parent.getSpace().getCode() + "/" + "NEW-SAMPLE";
@@ -251,7 +256,7 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest
             dataSetUpdates.add(updates);
         }
 
-        performDataSetUpdates(dataSetUpdates);
+        performDataSetUpdates(dataSetUpdates, 1);
 
         // Now retrieve the sample again and check that the properties were updated.
         List<ExternalData> updatedDataSets =
@@ -284,7 +289,21 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest
 
     }
 
-    private void performDataSetUpdates(List<DataSetBatchUpdatesDTO> dataSetUpdates)
+    @Test(expectedExceptions = EnvironmentFailureException.class)
+    public void testPerformEntityOperationsUpdateStaleDataSet()
+    {
+        ExternalData dataSetToUpdate = findDatasetByCode("20081105092159188-3");
+
+        DataSetBatchUpdatesDTO update =
+                createDataSetUpdateDTO(dataSetToUpdate, "COMMENT",
+                        "This is a new comment. This is not the old comment.");
+
+        update.setVersion(new Date());
+        performDataSetUpdates(Arrays.asList(update), 1);
+    }
+
+    private void performDataSetUpdates(List<DataSetBatchUpdatesDTO> dataSetUpdates,
+            Integer batchSizeOrNull)
     {
         TechId registrationid = new TechId(service.drawANewUniqueID(sessionToken));
         List<NewSpace> spaceRegistrations = Collections.emptyList();
@@ -299,19 +318,17 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest
                 new AtomicEntityOperationDetails(registrationid, null, spaceRegistrations,
                         projectRegistrations, experimentRegistrations, sampleUpdates,
                         sampleRegistrations, materialRegistrations, dataSetRegistrations,
-                        dataSetUpdates);
+                        dataSetUpdates, batchSizeOrNull);
         service.performEntityOperations(sessionToken, details);
     }
 
-    private void performSampleUpdate(Sample sampleToUpdate)
+    private void performSampleUpdate(List<SampleUpdatesDTO> sampleUpdates, Integer batchSizeOrNull)
     {
         TechId registrationid = new TechId(service.drawANewUniqueID(sessionToken));
         List<NewSpace> spaceRegistrations = Collections.emptyList();
         List<NewProject> projectRegistrations = Collections.emptyList();
         List<NewExperiment> experimentRegistrations = Collections.emptyList();
 
-        SampleUpdatesDTO sampleUpdate = convertToSampleUpdateDTO(sampleToUpdate);
-        List<SampleUpdatesDTO> sampleUpdates = Arrays.asList(sampleUpdate);
         List<NewSample> sampleRegistrations = Collections.emptyList();
         Map<String, List<NewMaterial>> materialRegistrations = Collections.emptyMap();
         List<? extends NewExternalData> dataSetRegistrations = Collections.emptyList();
@@ -320,7 +337,7 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest
                 new AtomicEntityOperationDetails(registrationid, null, spaceRegistrations,
                         projectRegistrations, experimentRegistrations, sampleUpdates,
                         sampleRegistrations, materialRegistrations, dataSetRegistrations,
-                        dataSetUpdates);
+                        dataSetUpdates, batchSizeOrNull);
         service.performEntityOperations(sessionToken, details);
     }
 
@@ -479,4 +496,33 @@ public class ETLServiceDatabaseTest extends AbstractDAOTest
 
         assertFalse(result.contains("/TESTGROUP/SAMPLE_EXAMPLE"));
     }
+
+    private Sample findSampleByCode(String code)
+    {
+        SearchCriteria searchCriteria = new SearchCriteria();
+        searchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch(
+                MatchClauseAttribute.CODE, code));
+        List<Sample> samples = service.searchForSamples(sessionToken, searchCriteria);
+        assertEquals(1, samples.size());
+        return samples.get(0);
+    }
+
+    private ExternalData findDatasetByCode(String code)
+    {
+        SearchCriteria searchCriteria = new SearchCriteria();
+        searchCriteria.addMatchClause(SearchCriteria.MatchClause.createAttributeMatch(
+                MatchClauseAttribute.CODE, code));
+        List<ExternalData> dataSets = service.searchForDataSets(sessionToken, searchCriteria);
+        assertEquals(1, dataSets.size());
+        return dataSets.get(0);
+    }
+
+    private void updateEntityProperty(IEntityPropertiesHolder entity, String propertyName,
+            String propertyValue)
+    {
+        String oldValue = EntityHelper.tryFindPropertyValue(entity, propertyName);
+        assertFalse(propertyValue.equals(oldValue));
+        EntityHelper.createOrUpdateProperty(entity, propertyName, propertyValue);
+    }
+
 }