Search code examples
c#microsoft-sync-framework

Microsoft Sync Framework: Metadata Store Replica is Already in Use


A bit of platform info - I'm using Visual Studio 2015, Windows 10 Pro, and the newest version of MS Sync Framework from Nuget.

using Microsoft.Synchronization;
using Microsoft.Synchronization.Files;
using System.IO;

namespace SyncEngine_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            string myDocsPath = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            string Dir1 = Path.Combine(myDocsPath, "Dir1");
            string Dir2 = Path.Combine(myDocsPath, "Dir2");
            string metadataPath = Path.Combine(myDocsPath, "Metadata");
            string tempDir = Path.Combine(myDocsPath, "Cache");
            string trashDir = Path.Combine(myDocsPath, "Trash");
            FileSyncScopeFilter filter = new FileSyncScopeFilter();
            filter.FileNameExcludes.Add("*.metadata");
            FileSyncOptions options = FileSyncOptions.None;

            //DetectChanges
            DetectChangesOnFileSystemReplica(Dir1, filter, options, Dir1, "filesync.metadata", tempDir, trashDir);
            DetectChangesOnFileSystemReplica(Dir2, filter, options, Dir2, "filesync.metadata", tempDir, trashDir);

            //SyncChanges Both Ways
            SyncFileSystemReplicasOneWay(Dir1, Dir2, filter, options, Dir1, "filesync.metadata", tempDir, trashDir);
            SyncFileSystemReplicasOneWay(Dir2, Dir1, filter, options, Dir2, "filesync.metadata", tempDir, trashDir);
        }

        public static void DetectChangesOnFileSystemReplica(string replicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options, string metadataPath, string metadataFile, string tempDir, string trashDir)
        {
            FileSyncProvider provider = null;
            try
            {
                provider = new FileSyncProvider(replicaRootPath, filter, options, metadataPath, metadataFile, tempDir, trashDir);
                provider.DetectChanges();
            }
            finally
            {
                // Release resources
                if (provider != null)
                    provider.Dispose();
            }
        }
        public static void SyncFileSystemReplicasOneWay(string sourceReplicaRootPath, string destinationReplicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options, string metadataPath, string metadataFile, string tempDir, string trashDir)
        {
            FileSyncProvider sourceProvider = null;
            FileSyncProvider destinationProvider = null;
            try
            {
                sourceProvider = new FileSyncProvider(sourceReplicaRootPath, filter, options, metadataPath, metadataFile, tempDir, trashDir);
                destinationProvider = new FileSyncProvider(destinationReplicaRootPath, filter, options, metadataPath, metadataFile, tempDir, trashDir);

                destinationProvider.AppliedChange += new EventHandler<AppliedChangeEventArgs>(OnAppliedChange);
                destinationProvider.SkippedChange += new EventHandler<SkippedChangeEventArgs>(OnSkippedChange);

                SyncOrchestrator agent = new SyncOrchestrator();
                agent.LocalProvider = sourceProvider;
                agent.RemoteProvider = destinationProvider;
                agent.Direction = SyncDirectionOrder.Upload; // Sync source to destination
                Console.WriteLine("Synchronizing changes to replica: " + destinationProvider.RootDirectoryPath);
                agent.Synchronize();
            }
            finally
            {
                // Release resources
                if (sourceProvider != null) sourceProvider.Dispose();
                if (destinationProvider != null) destinationProvider.Dispose();
            }
        }
    }

I'm trying to write a custom file sync program and I'm running into issues whenever I try to deviate from the default example per Microsoft. By default, when checking for changes and syncing, a file named filesync.metadata is created in the roots of the source and destination. The FileSyncProvider supports specifying a custom medadata path (which cannot be null) and along with that, custom cache/trash paths. This is what I'm really after, but I have to specify a specific metadata location and filename.

When I run it in this fashion, I get an exception that the Metadata Store Replica is Already in Use. This happens when I specify in a different folder, different names, same names, and even the default name in the default path. Here is a sample of my code:

I've seen this error reported before, but never with a good solution which concerns me a little bit. I don't know if I'm doing something wrong, if there's an issue with the framework that has never been addressed, or if Windows 10 is the culprit.

Again, the default usage works just fine, but I can't set custom paths:

            // Default Implementation - WHICH WORKS
            // Explicitly detect changes on both replicas upfront, to avoid two change 
            // detection passes for the two-way sync

            DetectChangesOnFileSystemReplica(replica1RootPath, filter, options);
            DetectChangesOnFileSystemReplica(replica2RootPath, filter, options);

            // Sync in both directions

            SyncFileSystemReplicasOneWay(replica1RootPath, replica2RootPath, filter, options);
            SyncFileSystemReplicasOneWay(replica2RootPath, replica1RootPath, filter, options);

I'm open to any suggestions, even alternatives to Microsoft Sync at this point. Thanks for your help!


Solution

  • Well sometimes you just need to step away to realize you made a very silly oversight. The way the SyncFileSystemReplicasOneWay() void was created was causing my destinationProvider to use the exact same metadata file/directory. I've modified the method parameters like so:

    Old way - doesn't work:

    public static void SyncFileSystemReplicasOneWay(string sourceReplicaRootPath, string destinationReplicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options, string metadataPath, string metadataFile, string tempDir, string trashDir){}
    

    Correct way - note the addition of file names and folder paths for BOTH metadata files:

    public static void SyncFileSystemReplicasOneWay(string sourceReplicaRootPath, string destinationReplicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options, string metadataPath1, string metadataFile1, string metadataPath2,string metadataFile2, string tempDir, string trashDir) {}
    

    Here's a larger snippet showing the creation of the source and destination provider object:

        public static void SyncFileSystemReplicasOneWay(string sourceReplicaRootPath, string destinationReplicaRootPath, FileSyncScopeFilter filter, FileSyncOptions options, string metadataPath1, string metadataFile1, string metadataPath2, string metadataFile2, string tempDir, string trashDir)
        {
            sourceProvider = new FileSyncProvider(sourceReplicaRootPath, filter, options, metadataPath1, metadataFile1, tempDir, trashDir);
            destinationProvider = new FileSyncProvider(destinationReplicaRootPath, filter, options, metadataPath2, metadataFile2, tempDir, trashDir);
        }
    

    So a careless oversight on my part. At least it is working perfectly now.