Skip to content

Commit

Permalink
Retry operations relying on parent node id with reset cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
dkocher committed Jan 25, 2025
1 parent 3abc114 commit 0dc29b6
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ public SDSCopyFeature(final SDSSession session, final SDSNodeIdProvider nodeid)
@Override
public Path copy(final Path source, final Path target, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException {
try {
new NodesApi(session.getClient()).copyNodes(
nodeid.retry(target.getParent(), () -> new NodesApi(session.getClient()).copyNodes(
new CopyNodesRequest()
.resolutionStrategy(CopyNodesRequest.ResolutionStrategyEnum.OVERWRITE)
.addItemsItem(new CopyNode().id(Long.parseLong(nodeid.getVersionId(source))))
.keepShareLinks(new HostPreferences(session.getHost()).getBoolean("sds.upload.sharelinks.keep")),
// Target Parent Node ID
Long.parseLong(nodeid.getVersionId(target.getParent())),
StringUtils.EMPTY, null);
StringUtils.EMPTY, null));
listener.sent(status.getLength());
nodeid.cache(target, null);
final PathAttributes attributes = new SDSAttributesFinderFeature(session, nodeid).find(target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ public SDSDirectS3MultipartWriteFeature(final SDSSession session, final SDSNodeI
@Override
public HttpResponseOutputStream<Node> write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
try {
final CreateFileUploadRequest createFileUploadRequest = new CreateFileUploadRequest()
final CreateFileUploadRequest createFileUploadRequest = nodeid.retry(file.getParent(), () -> new CreateFileUploadRequest()
.directS3Upload(true)
.timestampModification(status.getModified() != null ? new DateTime(status.getModified()) : null)
.timestampCreation(status.getCreated() != null ? new DateTime(status.getCreated()) : null)
.parentId(Long.parseLong(nodeid.getVersionId(file.getParent())))
.name(file.getName());
.name(file.getName()));
final CreateFileUploadResponse createFileUploadResponse = new NodesApi(session.getClient())
.createFileUploadChannel(createFileUploadRequest, StringUtils.EMPTY);
log.debug("upload started for {} with response {}", file, createFileUploadResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ public Node upload(final Path file, final Local local, final BandwidthThrottle t
else {
in = local.getInputStream();
}
final CreateFileUploadRequest createFileUploadRequest = new CreateFileUploadRequest()
final CreateFileUploadRequest createFileUploadRequest = nodeid.retry(file.getParent(), () -> new CreateFileUploadRequest()
.directS3Upload(true)
.timestampModification(status.getModified() != null ? new DateTime(status.getModified()) : null)
.timestampCreation(status.getCreated() != null ? new DateTime(status.getCreated()) : null)
.size(TransferStatus.UNKNOWN_LENGTH == status.getLength() ? null : status.getLength())
.parentId(Long.parseLong(nodeid.getVersionId(file.getParent())))
.name(file.getName());
.name(file.getName()));
final CreateFileUploadResponse createFileUploadResponse = new NodesApi(session.getClient())
.createFileUploadChannel(createFileUploadRequest, StringUtils.EMPTY);
log.debug("upload started for {} with response {}", file, createFileUploadResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@ public Path mkdir(final Path folder, final TransferStatus status) throws Backgro
}

private Path createFolder(final Path folder) throws BackgroundException, ApiException {
final CreateFolderRequest folderRequest = new CreateFolderRequest();
folderRequest.setParentId(Long.parseLong(nodeid.getVersionId(folder.getParent())));
folderRequest.setName(folder.getName());
final Node node = new NodesApi(session.getClient()).createFolder(folderRequest, StringUtils.EMPTY, null);
final Node node = nodeid.retry(folder.getParent(), () -> new NodesApi(session.getClient()).createFolder(new CreateFolderRequest()
.parentId(Long.parseLong(nodeid.getVersionId(folder.getParent())))
.name(folder.getName()), StringUtils.EMPTY, null));
nodeid.cache(folder, String.valueOf(node.getId()));
return folder.withAttributes(new SDSAttributesAdapter(session).toAttributes(node));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ public Path move(final Path file, final Path renamed, final TransferStatus statu
}
else {
// Move to different parent
new NodesApi(session.getClient()).moveNodes(
nodeid.retry(renamed.getParent(), () -> new NodesApi(session.getClient()).moveNodes(
new MoveNodesRequest()
.resolutionStrategy(MoveNodesRequest.ResolutionStrategyEnum.OVERWRITE)
.addItemsItem(new MoveNode().id(nodeId).name(renamed.getName()))
.keepShareLinks(new HostPreferences(session.getHost()).getBoolean("sds.upload.sharelinks.keep")),
Long.parseLong(nodeid.getVersionId(renamed.getParent())),
StringUtils.EMPTY, null);
StringUtils.EMPTY, null));
}
nodeid.cache(renamed, file.attributes().getVersionId());
nodeid.cache(file, null);
Expand Down
24 changes: 24 additions & 0 deletions dracoon/src/main/java/ch/cyberduck/core/sds/SDSNodeIdProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@
import ch.cyberduck.core.unicode.UnicodeNormalizer;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.concurrent.Callable;

public class SDSNodeIdProvider extends CachingVersionIdProvider implements VersionIdProvider {
private static final Logger log = LogManager.getLogger(SDSNodeIdProvider.class);

Expand Down Expand Up @@ -107,4 +110,25 @@ protected String getNodeId(final Path file, final int chunksize) throws Backgrou
throw new SDSExceptionMappingService(this).map("Failure to read attributes of {0}", e, file);
}
}

public <V> V retry(final Path file, final ApiExceptionCallable<V> callable) throws ApiException, BackgroundException {
try {
return callable.call();
}
catch(ApiException e) {
switch(e.getCode()) {
case HttpStatus.SC_NOT_FOUND:
// Parent directory not found. Try again with resetting node id cache
this.cache(file, null);
log.warn("Retry {}", callable);
return callable.call();
}
throw e;
}
}

public interface ApiExceptionCallable<T> extends Callable<T> {
@Override
T call() throws ApiException, BackgroundException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void revert(final Path file) throws BackgroundException {
.resolutionStrategy(RestoreDeletedNodesRequest.ResolutionStrategyEnum.OVERWRITE)
.keepShareLinks(new HostPreferences(session.getHost()).getBoolean("sds.upload.sharelinks.keep"))
.addDeletedNodeIdsItem(Long.parseLong(nodeid.getVersionId(file)))
.parentId(Long.parseLong(nodeid.getVersionId(file.getParent()))), StringUtils.EMPTY);
.parentId(Long.parseLong(nodeid.getVersionId(file.getParent()))), StringUtils.EMPTY);//todo
}
catch(ApiException e) {
throw new SDSExceptionMappingService(nodeid).map("Failure to write attributes of {0}", e, file);
Expand All @@ -81,10 +81,11 @@ public AttributedList<Path> list(final Path file, final ListProgressListener lis
DeletedNodeVersionsList nodes;
final AttributedList<Path> versions = new AttributedList<>();
do {
nodes = new NodesApi(session.getClient()).requestDeletedNodeVersions(
final int range = offset;
nodes = nodeid.retry(file.getParent(), () -> new NodesApi(session.getClient()).requestDeletedNodeVersions(
Long.parseLong(nodeid.getVersionId(file.getParent())),
file.isFile() ? "file" : "folder", file.getName(), StringUtils.EMPTY, "updatedAt:desc",
offset, chunksize, null);
range, chunksize, null));
for(DeletedNode item : nodes.getItems()) {
versions.add(new Path(file.getParent(), file.getName(), file.getType(),
new SDSAttributesAdapter(session).toAttributes(item)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
import ch.cyberduck.core.AlphanumericRandomStringService;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.RandomStringService;
import ch.cyberduck.core.exception.ConflictException;
import ch.cyberduck.core.features.Delete;
import ch.cyberduck.core.sds.io.swagger.client.api.NodesApi;
import ch.cyberduck.core.sds.io.swagger.client.model.CreateFolderRequest;
import ch.cyberduck.core.sds.io.swagger.client.model.Node;
import ch.cyberduck.core.shared.DefaultFindFeature;
import ch.cyberduck.core.transfer.TransferStatus;
import ch.cyberduck.test.IntegrationTest;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;

Expand All @@ -47,19 +50,30 @@ public void testCreateDirectory() throws Exception {
assertThrows(ConflictException.class, () -> new SDSDirectoryFeature(session, nodeid).mkdir(test, new TransferStatus()));
assertNotNull(test.attributes().getVersionId());
assertTrue(new DefaultFindFeature(session).find(test));
new SDSDeleteFeature(session, nodeid).delete(Arrays.asList(test, room), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new DefaultFindFeature(session).find(test));
// Replace directory on server with same name
new NodesApi(session.getClient()).removeNode(Long.parseLong(test.attributes().getVersionId()), StringUtils.EMPTY);
final CreateFolderRequest folderRequest = new CreateFolderRequest();
folderRequest.setParentId(Long.parseLong(room.attributes().getVersionId()));
folderRequest.setName(test.getName());
// New node for directory with same nmae
final Node node = new NodesApi(session.getClient()).createFolder(folderRequest, StringUtils.EMPTY, null);
assertNotEquals(test.attributes().getVersionId(), node.getId().toString());
// Attempt to create subdirectory referencing previous node id
final Path subdir = new SDSDirectoryFeature(session, nodeid).mkdir(new Path(test,
new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus());
assertEquals(node.getId().toString(), nodeid.getVersionId(test));
assertEquals(test.attributes().getVersionId(), node.getId().toString());
new SDSDeleteFeature(session, nodeid).delete(Arrays.asList(subdir, test, room), new DisabledLoginCallback(), new Delete.DisabledCallback());
}

@Test
public void testCreateDataRoom() throws Exception {
final RandomStringService randomStringService = new AlphanumericRandomStringService();
final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session);
final Path room = new SDSDirectoryFeature(session, nodeid).mkdir(
new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus());
assertNotNull(room.attributes().getVersionId());
assertTrue(new DefaultFindFeature(session).find(room));
new SDSDeleteFeature(session, nodeid).delete(Collections.<Path>singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback());
new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback());
assertFalse(new DefaultFindFeature(session).find(room));
}
}

0 comments on commit 0dc29b6

Please sign in to comment.