-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CHE-1143: fixed synchronization issue during removal of several projects #1343
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -92,11 +92,17 @@ | |
import java.util.HashSet; | ||
import java.util.LinkedHashMap; | ||
import java.util.LinkedHashSet; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Scanner; | ||
import java.util.Set; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.Future; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
import java.util.zip.ZipEntry; | ||
import java.util.zip.ZipOutputStream; | ||
|
||
|
@@ -219,39 +225,23 @@ public void setUp() throws Exception { | |
FileWatcherNotificationHandler fileWatcherNotificationHandler = new DefaultFileWatcherNotificationHandler(vfsProvider); | ||
FileTreeWatcher fileTreeWatcher = new FileTreeWatcher(root, new HashSet<>(), fileWatcherNotificationHandler); | ||
|
||
pm = new ProjectManager(vfsProvider, null, ptRegistry, projectRegistry, phRegistry, | ||
pm = new ProjectManager(vfsProvider, new EventService(), ptRegistry, projectRegistry, phRegistry, | ||
importerRegistry, fileWatcherNotificationHandler, fileTreeWatcher, workspaceHolder); | ||
pm.initWatcher(); | ||
|
||
HttpJsonRequest httpJsonRequest = mock(HttpJsonRequest.class, new SelfReturningAnswer()); | ||
|
||
//List<ProjectConfigDto> modules = new ArrayList<>(); | ||
|
||
final ProjectConfigDto testProjectConfigMock = mock(ProjectConfigDto.class); | ||
when(testProjectConfigMock.getPath()).thenReturn("/my_project"); | ||
when(testProjectConfigMock.getName()).thenReturn("my_project"); | ||
when(testProjectConfigMock.getDescription()).thenReturn("my test project"); | ||
when(testProjectConfigMock.getType()).thenReturn("my_project_type"); | ||
// when(testProjectConfigMock.getModules()).thenReturn(modules); | ||
when(testProjectConfigMock.getSource()).thenReturn(DtoFactory.getInstance().createDto(SourceStorageDto.class)); | ||
// when(testProjectConfigMock.findModule(anyString())).thenReturn(testProjectConfigMock); | ||
|
||
Map<String, List<String>> attr = new HashMap<>(); | ||
for (Attribute attribute : myProjectType.getAttributes()) { | ||
attr.put(attribute.getName(), attribute.getValue().getList()); | ||
} | ||
when(testProjectConfigMock.getAttributes()).thenReturn(attr); | ||
|
||
projects = new ArrayList<>(); | ||
projects.add(testProjectConfigMock); | ||
addMockedProjectConfigDto(myProjectType, "my_project"); | ||
|
||
when(httpJsonRequestFactory.fromLink(any())).thenReturn(httpJsonRequest); | ||
when(httpJsonRequest.request()).thenReturn(httpJsonResponse); | ||
when(httpJsonResponse.asDto(WorkspaceDto.class)).thenReturn(usersWorkspaceMock); | ||
when(usersWorkspaceMock.getConfig()).thenReturn(workspaceConfigMock); | ||
when(workspaceConfigMock.getProjects()).thenReturn(projects); | ||
|
||
pm.createProject(testProjectConfigMock, null); | ||
|
||
// verify(httpJsonRequestFactory).fromLink(eq(DtoFactory.newDto(Link.class) | ||
// .withHref(apiEndpoint + "/workspace/" + workspace + "/project") | ||
// .withMethod(PUT))); | ||
|
@@ -288,6 +278,27 @@ public Set<Object> getSingletons() { | |
env = org.eclipse.che.commons.env.EnvironmentContext.getCurrent(); | ||
} | ||
|
||
private void addMockedProjectConfigDto(org.eclipse.che.api.project.server.type.ProjectTypeDef myProjectType, String projectName) | ||
throws ForbiddenException, ServerException, NotFoundException, ConflictException { | ||
final ProjectConfigDto testProjectConfigMock = mock(ProjectConfigDto.class); | ||
when(testProjectConfigMock.getPath()).thenReturn("/" + projectName); | ||
when(testProjectConfigMock.getName()).thenReturn(projectName); | ||
when(testProjectConfigMock.getDescription()).thenReturn("my test project"); | ||
when(testProjectConfigMock.getType()).thenReturn("my_project_type"); | ||
when(testProjectConfigMock.getSource()).thenReturn(DtoFactory.getInstance().createDto(SourceStorageDto.class)); | ||
// when(testProjectConfigMock.getModules()).thenReturn(modules); | ||
// when(testProjectConfigMock.findModule(anyString())).thenReturn(testProjectConfigMock); | ||
|
||
Map<String, List<String>> attr = new HashMap<>(); | ||
for (Attribute attribute : myProjectType.getAttributes()) { | ||
attr.put(attribute.getName(), attribute.getValue().getList()); | ||
} | ||
when(testProjectConfigMock.getAttributes()).thenReturn(attr); | ||
|
||
projects.add(testProjectConfigMock); | ||
|
||
pm.createProject(testProjectConfigMock, null); | ||
} | ||
|
||
@Test | ||
@SuppressWarnings("unchecked") | ||
|
@@ -927,6 +938,50 @@ public void testDeleteProject() throws Exception { | |
pm.getProject("my_project"); | ||
} | ||
|
||
@Test | ||
public void testDeleteProjectsConcurrently() throws Exception { | ||
int threadNumber = 100; | ||
ExecutorService executor = Executors.newFixedThreadPool(threadNumber); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you sure about non-daemon executor which is never shut down? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well you're right, though in this very case it should not necessarily be shut down except for the reason of reclamation of its resources, but I guess I will add a call to make the code more clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can make them daemons There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure |
||
CountDownLatch countDownLatch = new CountDownLatch(threadNumber); | ||
List<Future<ContainerResponse>> futures = new LinkedList<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you know the size of the list i'd say that it is better to choose There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see no significant benefit of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Array list is almost always faster There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well it is not a significant one but yes i think i do, it is all about the heap and objects location in it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still don't see the point. In this particular case we are adding and iterating. For these operations Not sure I can get what you mean saying
Generally speaking if we talk about optimization I find it quite unusual that you did not mention using java arrays for example. Looks like you are arguing just for a dispute. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unless you clearly want to use some features of a LinkedList, ArrayList do the work in a less complex way There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, obviously for me it is not a big deal to replace a I guess if I know the size of an array I definitely should use simple java array (taking into account that I need only set element and get iterator). I need to know how do you see the difference between well-optimized and over-optimized code, how do you see where optimization matters more than convenience. Where can I find any rules or conventions or you just impose your preferences? 😆 |
||
|
||
|
||
for (int i = 0; i < threadNumber; i++) { | ||
addMockedProjectConfigDto(ptRegistry.getProjectType("my_project_type"), "my_project_name" + i); | ||
} | ||
|
||
IntStream.range(0, threadNumber).forEach( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a reason why here you use forEach and just before you used a for (int i =0; loop) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 foreach looks odd here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the reason that stands by using of This situation would happen if I used simple loop. Do you see how we can make it less ugly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my concern was only to unify the two loops into a single loop. (because we iterate on the same numbers but in a different way) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exactly! As we can see in this article in general cases java streams are a bit slower than |
||
i -> { | ||
futures.add(executor.submit(() -> { | ||
countDownLatch.countDown(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, it seems that you don't have to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure that I got your idea, could you explain more? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea: await 100 times, count down once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please consider usage of barrier here instead of countdown latch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
http://stackoverflow.com/questions/4168772/java-concurrency-countdown-latch-vs-cyclic-barrier There is no need to run |
||
countDownLatch.await(); | ||
|
||
try { | ||
return launcher.service(DELETE, | ||
"http://localhost:8080/api/project/my_project_name" + i, | ||
"http://localhost:8080/api", null, null, null); | ||
} catch (Exception e) { | ||
throw new IllegalStateException(e); | ||
} | ||
})); | ||
} | ||
); | ||
|
||
boolean isNotDone; | ||
do { | ||
isNotDone = false; | ||
for (Future<ContainerResponse> future : futures) { | ||
if (!future.isDone()) { | ||
isNotDone = true; | ||
} | ||
} | ||
} while (isNotDone); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can do it clearer and more gracefully by using another There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have deep concerns that using
is clearer than a simple loop. Unless you can prove it? |
||
|
||
for (Future<ContainerResponse> future : futures) { | ||
assertEquals(future.get().getStatus(), 204, "Error: " + future.get().getEntity()); | ||
} | ||
} | ||
|
||
@Test | ||
public void testCopyFile() throws Exception { | ||
RegisteredProject myProject = pm.getProject("my_project"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have you added commented code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just extracted the sources into a separate method and those comments already were there. Unfortunately there is no information about the author of that ugly code so I'm still not sure if that was left for some reason or by a mistake.
As I'm quite a new person to this very project I feel some responsibility for the legacy sources, so I think that it is yet not of my competence to remove someones comments.
Anyway if you feel confident and recommend to remove it - that should not be a problem at all ;-)