Skip to content

Commit

Permalink
Provisioning parallelism tests - work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Aug 28, 2017
1 parent ce0d39c commit 7ed0050
Show file tree
Hide file tree
Showing 13 changed files with 736 additions and 47 deletions.
Expand Up @@ -97,6 +97,7 @@ public class DummyResource implements DebugDumpable {
private boolean caseIgnoreId = false;
private boolean caseIgnoreValues = false;
private int connectionCount = 0;
private int writeOperationCount = 0;
private int groupMembersReadCount = 0;
private Collection<String> forbiddenNames;

Expand Down Expand Up @@ -376,6 +377,14 @@ public synchronized void disconnect() {
public void assertNoConnections() {
assert connectionCount == 0 : "Dummy resource: "+connectionCount+" connections still open";
}

public synchronized void recordWriteOperation(String operation) {
writeOperationCount++;
}

public int getWriteOperationCount() {
return writeOperationCount;
}

public int getGroupMembersReadCount() {
return groupMembersReadCount;
Expand Down Expand Up @@ -535,6 +544,7 @@ public Collection<DummyOrg> listOrgs() throws ConnectException, FileNotFoundExce

private synchronized <T extends DummyObject> String addObject(Map<String,T> map, T newObject) throws ObjectAlreadyExistsException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException {
checkBlockOperations();
recordWriteOperation("add");
breakIt(addBreakMode, "add");

Class<? extends DummyObject> type = newObject.getClass();
Expand Down Expand Up @@ -583,6 +593,7 @@ private synchronized <T extends DummyObject> String addObject(Map<String,T> map,

private synchronized <T extends DummyObject> void deleteObjectByName(Class<T> type, Map<String,T> map, String name) throws ObjectDoesNotExistException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException {
checkBlockOperations();
recordWriteOperation("delete");
breakIt(deleteBreakMode, "delete");

String normalName = normalize(name);
Expand Down Expand Up @@ -625,6 +636,7 @@ public void deleteOrgById(String id) throws ConnectException, FileNotFoundExcept

private synchronized <T extends DummyObject> void deleteObjectById(Class<T> type, Map<String,T> map, String id) throws ObjectDoesNotExistException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException {
checkBlockOperations();
recordWriteOperation("delete");
breakIt(deleteBreakMode, "delete");

DummyObject object = allObjects.get(id);
Expand Down Expand Up @@ -661,6 +673,7 @@ private synchronized <T extends DummyObject> void deleteObjectById(Class<T> type

private <T extends DummyObject> void renameObject(Class<T> type, Map<String,T> map, String id, String oldName, String newName) throws ObjectDoesNotExistException, ObjectAlreadyExistsException, ConnectException, FileNotFoundException, SchemaViolationException, ConflictException {
checkBlockOperations();
recordWriteOperation("modify");
breakIt(modifyBreakMode, "modify");

T existingObject;
Expand Down Expand Up @@ -753,6 +766,7 @@ public void renameOrg(String id, String oldName, String newName) throws ObjectDo
}

void recordModify(DummyObject dObject) {
recordWriteOperation("modify");
if (syncStyle != DummySyncStyle.NONE) {
int syncToken = nextSyncToken();
DummyDelta delta = new DummyDelta(syncToken, dObject.getClass(), dObject.getId(), dObject.getName(), DummyDeltaType.MODIFY);
Expand Down
Expand Up @@ -4551,6 +4551,40 @@
in combination with shadowConstraintsCheck to reduce probability of identifier
conflicts for resources with slow create/rename operations.

See also avoidDuplicateOperations and recordPendingOperations properties.

This feature is EXPERIMENTAL. Use with care.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.6.1</a:since>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="recordPendingOperations" type="tns:RecordPendingOperationsType" minOccurs="0" default="asynchronous">
<xsd:annotation>
<xsd:documentation>
TODO

See also avoidDuplicateOperations and useProposedShadows properties.

This feature is EXPERIMENTAL. Use with care.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.6.1</a:since>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="avoidDuplicateOperations" type="xsd:boolean" minOccurs="0" default="false">
<xsd:annotation>
<xsd:documentation>
When set to true, midPoint will try to avoid executing duplication operations
on the resource. If an operation is already underway the duplicate operation
will be ignored.

See also recordPendingOperations and useProposedShadows properties.

This feature is EXPERIMENTAL. Use with care.
</xsd:documentation>
<xsd:appinfo>
Expand Down Expand Up @@ -4665,6 +4699,45 @@
</xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="RecordPendingOperationsType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumClass/>
<a:since>3.6.1</a:since>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="asynchronous">
<xsd:annotation>
<xsd:documentation>
Record only asynchronous operations. The operation
will be recorded when we know that it is asynchronous.
Which usually means it is only recoded after the operation
is started.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ASYNCHRONOUS"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="all">
<xsd:annotation>
<xsd:documentation>
Record all operations to pending deltas. The operations
are recoreded even before they are started.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ALL"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="ErrorSelectorType">
<xsd:annotation>
<xsd:documentation>
Expand Down
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.test.util;

/**
* @author semancik
*
*/
public class Counter {
private int count = 0;

public int getCount() {
return count;
}

public synchronized void click() {
count++;
}

public void assertCount(int expectedCount) {
assert count == expectedCount : "Wrong counter, expected "+expectedCount+", was "+count;
}

public void assertCount(String message, int expectedCount) {
assert count == expectedCount : message + ", expected "+expectedCount+", was "+count;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + count;
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Counter other = (Counter) obj;
if (count != other.count) {
return false;
}
return true;
}

@Override
public String toString() {
return "Counter(" + count + ")";
}


}
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2016 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.test.util;

import com.evolveum.midpoint.util.FailableRunnable;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;

/**
* @author semancik
*
*/
public class ParallelTestThread extends Thread {

private static final Trace LOGGER = TraceManager.getTrace(ParallelTestThread.class);

private FailableRunnable target;
private Throwable exception;

public ParallelTestThread(FailableRunnable target) {
super();
this.target = target;
}

@Override
public void run() {
try {
target.run();
} catch (RuntimeException | Error e) {
recordException(e);
throw e;
} catch (Throwable e) {
recordException(e);
throw new SystemException(e.getMessage(), e);
}
}

public Throwable getException() {
return exception;
}

public void recordException(Throwable e) {
LOGGER.error("Test thread failed: {}", e.getMessage(), e);
this.exception = e;
}

}
Expand Up @@ -36,6 +36,7 @@
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.util.FailableRunnable;
import com.evolveum.midpoint.util.exception.*;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
Expand Down Expand Up @@ -75,6 +76,7 @@
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.IntegrationTestTools;
import com.evolveum.midpoint.test.util.ParallelTestThread;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
Expand Down Expand Up @@ -186,9 +188,6 @@ public abstract class AbstractManualResourceTest extends AbstractConfiguredModel

protected static final String INTEREST_ONE = "one";

protected static final Random RND = new Random();


protected PrismObject<ResourceType> resource;
protected ResourceType resourceType;

Expand Down Expand Up @@ -2912,42 +2911,29 @@ public void test900ConcurrentConstructions() throws Exception {
Task task = createTask(TEST_NAME);
OperationResult result = task.getResult();

final int THREADS = getConcurrentTestNumberOfThreads();
final long TIMEOUT = 60000L;

// WHEN
displayWhen(TEST_NAME);
Thread[] threads = new Thread[THREADS];
for (int i = 0; i < THREADS; i++) {
threads[i] = new Thread(() -> {
try {
Thread.sleep(RND.nextInt(getConcurrentTestRandomStartDelayRange())); // Random start delay
LOGGER.info("{} starting", Thread.currentThread().getName());

ParallelTestThread[] threads = multithread(TEST_NAME,
() -> {
login(userAdministrator);
Task localTask = createTask(TEST_NAME + ".local");

assignAccount(USER_BARBOSSA_OID, getResourceOid(), SchemaConstants.INTENT_DEFAULT, localTask, localTask.getResult());
} catch (CommonException | InterruptedException e) {
throw new SystemException("Couldn't assign resource: " + e.getMessage(), e);
}
});
threads[i].setName("Thread " + (i+1) + " of " + THREADS);
threads[i].start();
}


}, getConcurrentTestNumberOfThreads(), getConcurrentTestRandomStartDelayRange());

// THEN
displayThen(TEST_NAME);
for (int i = 0; i < THREADS; i++) {
if (threads[i].isAlive()) {
System.out.println("Waiting for " + threads[i]);
threads[i].join(TIMEOUT);
}
}
waitForThreads(threads, TIMEOUT);

PrismObject<UserType> barbossa = getUser(USER_BARBOSSA_OID);
display("barbossa", barbossa);
assertEquals("Wrong # of links", 1, barbossa.asObjectable().getLinkRef().size());
}

protected int getConcurrentTestNumberOfThreads() {
return 4;
}
Expand Down
Expand Up @@ -583,6 +583,10 @@ public String addShadow(PrismObject<ShadowType> shadow, OperationProvisioningScr
isDoDiscovery(resource, options), true, parentResult);
return null;
}
if (!(attributesContainer instanceof ResourceAttributeContainer)) {
applyAttributesDefinition(ctx, shadow);
attributesContainer = shadow.findContainer(ShadowType.F_ATTRIBUTES);
}

preAddChecks(ctx, shadow, task, parentResult);

Expand Down Expand Up @@ -614,7 +618,7 @@ public String addShadow(PrismObject<ShadowType> shadow, OperationProvisioningScr
}

// REPO OPERATION: add
// This is where the repo shadow is created (if needed)
// This is where the repo shadow is created or updated (if needed)
String oid = afterAddOnResource(ctx, shadowOid, asyncReturnValue, parentResult);
addedShadow.setOid(oid);

Expand Down

0 comments on commit 7ed0050

Please sign in to comment.