ShareFile-NET icon indicating copy to clipboard operation
ShareFile-NET copied to clipboard

SDK performance vs REST API

Open alexandrugheorghiu opened this issue 10 years ago • 9 comments

I was trying to compare the SDK vs the REST API in terms of their speed while uploading a file. For this I've used:

  • 12 files ranging from 12K to 8.6M in size;
  • for the SKD I've used the code from the documentation: https://github.com/citrix/ShareFile-NET#upload;
  • for the REST API I've used the code from the sample: http://api.sharefile.com/rest/samples/csharp.aspx.

These are the results: image

It seems that the REST API is faster than the SDK for smaller files, but the SDK is faster than the REST API for the bigger files.

Some explanations with respect to these results would be highly appreciated.

Thank you.

alexandrugheorghiu avatar Sep 03 '15 18:09 alexandrugheorghiu

@alexandrugheorghiu Thanks for the detailed post.

I'll need to do some digging to see if where the issue is with the SDK. I suspect we may be paying a bit of a penalty on smaller files due to our attempt at controlling the flush of bytes to the network - we want to be in control to be able to provide appropriate progress events to the caller as a result we may not be getting as much data on the wire as we could.

For larger files we will break up segments into multiple requests and we quickly scale up the number of bytes we send per chunk per connection. This also supports concurrently uploading multiple parts of the file at the same time, I believe we are fixing this to 1 right now but could lift that limit (in internal testing we found 4 to be optimal).

I'll get back to you with what we find out about smaller files.

rgmills avatar Sep 06 '15 02:09 rgmills

@rgmills Thank you very much for your reply. Looking forward to an update with respect to your findings.

alexandrugheorghiu avatar Sep 07 '15 13:09 alexandrugheorghiu

@alexandrugheorghiu Here is our initial hunch:

Within the SDK we try to make an intelligent decision about which Upload API call to use, in order to do so we need to load the capabilities (subdomain.sf-api.com/sf/v3/Capabilities) for the provider if the SDK consumer has not already loaded them as part of the UploadSpecificationRequest (https://github.com/citrix/ShareFile-NET/blob/6fd6dee1326bd6fe1b4b1d8be6f6885777fff9c9/Core/Transfers/Uploaders/AsyncUploaderBase.cs#L32-L39). Unfortunately, we don't cache this :-1:

We'll be working on adding some light internal caching for capabilities by provider (i.e. sf) and host subdomain.sf-api.com - I'd expect an update this week. In the interim, you'll need to do the caching yourself and provide it on to UploadSpecificationRequest.ProviderCapabilities.

rgmills avatar Sep 09 '15 01:09 rgmills

Thank you. Having in mind that this is already in pre-production (for us) I will leave it as is and wait for an update. Looking forward to it.

alexandrugheorghiu avatar Sep 09 '15 12:09 alexandrugheorghiu

@alexandrugheorghiu NuGet (3.0.21) and GitHub have been updated with the latest

rgmills avatar Sep 11 '15 13:09 rgmills

@rgmills Thank you for the update. I've downloaded the new version and ran the same tests and then compared the results: image

  1. Any idea why the upload speed is slower (when comparing to previous version) for bigger files?
  2. Any idea why there is that exception (file 3.txt) in terms of download speeds (in my tests, that always occurs, for any of the first uploaded files)?

alexandrugheorghiu avatar Sep 11 '15 20:09 alexandrugheorghiu

@rgmills Re-ran all the tests today and compared the averages for 10 consecutive runs: image

(2) from previous post is gone (I assume it had to be some connectivity issue). But (1) still remains the biggest problem. Looking forward to any feedback with respect to this.

alexandrugheorghiu avatar Sep 14 '15 21:09 alexandrugheorghiu

@alexandrugheorghiu Would you be up for trying explicitly setting UploadSpecificationRequest.Method to Standard? Also, would you by chance have an isolated solution you could post for us to take a look at? If not, some psuedo code of what your tests look like would be great.

rgmills avatar Sep 15 '15 00:09 rgmills

@rgmills I'm not sure how to explicitly set UploadSpecificationRequest.Method to Standard. [Update: I've managed to set it to UploadMethod.Standard. Now 11.txt fails to upload (after 4 minutes it stops with "A task was canceled" exception.]

As for the testing, I don't have an isolated solution, but I've extracted some source code that I've pasted below:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Diagnostics = System.Diagnostics;

using ShareFile.Api.Client;
using ShareFile.Api.Client.Logging;
using ShareFile.Api.Client.Security.Authentication.OAuth2;
using ShareFile.Api.Client.Transfers;
using ShareFile.Api.Client.FileSystem;
using ShareFile.Api.Models;

namespace ShareFileApiTest
{
    public struct SampleUser
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string Subdomain { get; set; }
        public string ControlPlane { get; set; }
    }

    public class Test
    {
        private List<string> files = new List<string>() { "0.txt", "1.txt", "2.txt", "3.txt", "4.txt", "5.txt", "6.txt", "7.txt", "8.txt", "9.txt", "10.txt", "11.txt" };

        public void Run()
        {
            Directory.CreateDirectory("FromAPI");

            RunAPITest();

            Directory.Delete("FromAPI", true);

            Console.WriteLine("Press any key");
            Console.ReadLine();
        }

        private void RunAPITest()
        {
            var user = new SampleUser
            {
                ControlPlane = "sharefile.com",
                Username = "[email protected]",
                Password = "password",
                Subdomain = "subdomain"
            };

            string oauthClientId = "id";
            string oauthClientSecret = "secret";

            RunAPI(user, oauthClientId, oauthClientSecret).Wait();
        }

        private async Task RunAPI(SampleUser user, string clientId, string clientSecret)
        {
            // Authenticate with username/password
            var sfClient = await PasswordAuthentication(user, clientId, clientSecret);

            // Create a Session
            await StartSession(sfClient);

            // Load Shared Folder and Contents
            var sharedFolder = await LoadSharedFolderAndChildren(sfClient);

            // Create a Shared Folder
            var createdSharedFolder = await CreateFolder(sfClient, sharedFolder);

            // Upload a shared file
            Console.WriteLine();
            Console.WriteLine("Upload a shared file via API");
            List<Item> uploadedSharedFiles = new List<Item>();
            foreach (string fileName in this.files)
            {
                Diagnostics.Stopwatch timer = Diagnostics.Stopwatch.StartNew();

                var uploadedSharedFileId = await UploadViaApi(sfClient, createdSharedFolder, fileName);

                timer.Stop();
                TimeSpan timespan = timer.Elapsed;
                Console.WriteLine(String.Format("{3} - {4} bytes - {0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10, fileName, new FileInfo(fileName).Length));

                var sharedItemUri = sfClient.Items.GetAlias(uploadedSharedFileId);
                var uploadedSharedFile = await sfClient.Items.Get(sharedItemUri).ExecuteAsync();
                uploadedSharedFiles.Add(uploadedSharedFile);
            }

            // Download a shared file
            Console.WriteLine();
            Console.WriteLine("Download a shared file via API");
            foreach (Item uploadedSharedFile in uploadedSharedFiles)
            {
                Diagnostics.Stopwatch timer = Diagnostics.Stopwatch.StartNew();

                await DownloadViaApi(sfClient, uploadedSharedFile, "FromAPI");

                timer.Stop();
                TimeSpan timespan = timer.Elapsed;
                Console.WriteLine(String.Format("{3} - {4} bytes - {0:00}:{1:00}:{2:00}", timespan.Minutes, timespan.Seconds, timespan.Milliseconds / 10, uploadedSharedFile.Name, new FileInfo(uploadedSharedFile.Name).Length));
            }
        }

        private async Task<ShareFileClient> PasswordAuthentication(SampleUser user, string clientId, string clientSecret)
        {
            // Initialize ShareFileClient.
            var configuration = Configuration.Default();
            configuration.Logger = new DefaultLoggingProvider();

            var sfClient = new ShareFileClient("https://secure.sf-api.com/sf/v3/", configuration);
            var oauthService = new OAuthService(sfClient, clientId, clientSecret);

            // Perform a password grant request.  Will give us an OAuthToken
            var oauthToken = await oauthService.PasswordGrantAsync(user.Username, user.Password, user.Subdomain, user.ControlPlane);

            // Add credentials and update sfClient with new BaseUri
            sfClient.AddOAuthCredentials(oauthToken);
            sfClient.BaseUri = oauthToken.GetUri();

            return sfClient;
        }

        private async Task StartSession(ShareFileClient sfClient)
        {
            var session = await sfClient.Sessions.Login().Expand("Principal").ExecuteAsync();
            Console.WriteLine("Authenticated as " + session.Principal.Email);
        }

        public static async Task<Folder> LoadSharedFolderAndChildren(ShareFileClient sfClient)
        {
            var allSharedAlias = sfClient.Items.GetAlias("allshared");

            var sharedFolder = await sfClient.Items.Get(allSharedAlias).Expand("Children").ExecuteAsync();

            var folder = await GetFolderContents(sfClient, sharedFolder.url);

            return folder;
        }

        private async Task<Folder> GetFolderContents(ShareFileClient sfClient, Uri folderURL)
        {
            var folder = (Folder)await sfClient.Items.Get(folderURL).Expand("Children").ExecuteAsync();

            return folder;
        }

        private async Task<Folder> CreateFolder(ShareFileClient sfClient, Folder parentFolder)
        {
            var newFolder = new Folder
            {
                Name = "SDKTest",
                Description = "Created using the SDK"
            };

            return await sfClient.Items.CreateFolder(parentFolder.url, newFolder, overwrite: true).ExecuteAsync();
        }

        private async Task<string> UploadViaApi(ShareFileClient sfClient, Folder destinationFolder, string fileName)
        {
            var file = System.IO.File.Open(fileName, FileMode.OpenOrCreate);
            var uploadRequest = new UploadSpecificationRequest
            {
                FileName = fileName,
                FileSize = file.Length,
                Details = fileName,
                Parent = destinationFolder.url
            };

            var uploader = sfClient.GetAsyncFileUploader(uploadRequest,
                new PlatformFileStream(file, file.Length, fileName));

            var uploadResponse = await uploader.UploadAsync();

            return uploadResponse.First().Id;
        }

        private async Task DownloadViaApi(ShareFileClient sfClient, Item itemToDownload, string downloadFolderName)
        {
            var downloadDirectory = new DirectoryInfo(downloadFolderName);

            var downloader = sfClient.GetAsyncFileDownloader(itemToDownload);
            var file = System.IO.File.Open(Path.Combine(downloadDirectory.Name, itemToDownload.Name), FileMode.Create);

            await downloader.DownloadToAsync(file);
        }
    }
}

alexandrugheorghiu avatar Sep 15 '15 14:09 alexandrugheorghiu