Beispiel #1
0
 /// <summary>
 ///
 /// </summary>
 public StorageManager(List <StorageLocation> InLocations, ManifestDownloadManager InDownloadManager, BuildManifestRegistry Registry, AsyncIOQueue InIOQueue, ManifestStorageHeuristic Heuristic, List <Guid> KeepBuilds, List <Guid> DeleteBuilds)
 {
     IOQueue          = InIOQueue;
     ManifestRegistry = Registry;
     DownloadManager  = InDownloadManager;
     Locations        = InLocations;
     StoredLocations  = new List <StorageLocation>(Locations);
     StorageHeuristic = Heuristic;
     StoragePrioritizeKeepingBuildTagIds  = KeepBuilds;
     StoragePrioritizeDeletingBuildTagIds = DeleteBuilds;
 }
Beispiel #2
0
        /// <summary>
        /// </summary>
        /// <param name="Index"></param>
        /// <returns></returns>
        public bool GetBlockData(int Index, string RootPath, AsyncIOQueue IOQueue, byte[] Data, out long DataLength)
        {
            LazyCacheBlockInfo();

            DataLength = 0;

            if (Index < 0 || Index >= BlockCount)
            {
                throw new ArgumentOutOfRangeException("Index", "Block index out of range.");
                return(false);
            }

            BuildManifestBlockInfo Info = BlockInfo[Index];

            Debug.Assert(Data.Length >= Info.TotalSize);
            DataLength = Info.TotalSize;

            ManualResetEvent CompleteEvent = new ManualResetEvent(false);
            int QueuedReads = Info.SubBlocks.Length;

            bool Success = true;

            foreach (BuildManifestSubBlockInfo SubBlock in Info.SubBlocks)
            {
                string PathName = Path.Combine(RootPath, SubBlock.File.Path);
                IOQueue.Read(
                    PathName, SubBlock.FileOffset, SubBlock.FileSize, Data, SubBlock.OffsetInBlock, bSuccess =>
                {
                    int Result = Interlocked.Decrement(ref QueuedReads);
                    if (Result == 0)
                    {
                        CompleteEvent.Set();
                    }

                    if (!bSuccess)
                    {
                        Success = false;

                        Logger.Log(LogLevel.Error, LogCategory.Manifest, "Failed to read data for block (offset={0} size={1}) from file {2}", SubBlock.FileOffset, SubBlock.FileSize, SubBlock.File);
                    }
                }
                    );
            }

            CompleteEvent.WaitOne();

            return(Success);
        }
Beispiel #3
0
        /// <summary>
        /// </summary>
        public static void OnStart()
        {
            Statistic.Instantiate();

            ScmManager = new ScmManager();
            IOQueue    = new AsyncIOQueue();
            ManifestDownloadManager = new ManifestDownloadManager();

            DownloadManager = new DownloadManager();
            DownloadManager.OnRequestReplicatedBuilds += (List <Guid> SelectTags, List <Guid> IgnoreTags, DateTime NewerThan) =>
            {
                NetClient.RequestFilteredBuilds(SelectTags, IgnoreTags, NewerThan);

                Settings.ReplicationNewerThanTime = DownloadManager.ReplicationNewerThanTime;
                SaveSettings();
            };

            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Initializing settings");
            InitSettings();

            NetClient          = new Core.Client.Client();
            RemoteActionClient = new RemoteActionClient(NetClient, ManifestDownloadManager);

            TagRegistry   = new TagRegistry();
            RouteRegistry = new RouteRegistry(null, TagRegistry);
            BuildRegistry = new BuildManifestRegistry(TagRegistry);

            StorageManager = new StorageManager(Settings.StorageLocations, ManifestDownloadManager, BuildRegistry, IOQueue, Settings.StorageHeuristic, Settings.PrioritizeKeepingBuildTagIds, Settings.PrioritizeDeletingBuildTagIds);

            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Setting up network client");
            BuildRegistry.Open(Path.Combine(AppDataDir, "Manifests"), int.MaxValue);

            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Setting up network client");
            NetClient.Start(
                Settings.ServerHostname,
                Settings.ServerPort,
                Settings.ClientPortRangeMin,
                Settings.ClientPortRangeMax,
                Settings.AllowRemoteActions,
                Settings.TagIds,
                BuildRegistry,
                StorageManager,
                ManifestDownloadManager,
                TagRegistry,
                RouteRegistry
                );
            NetClient.TagIds = new List <Guid>(Settings.TagIds);

            // Setup the virtual file system we will store our available builds in.
            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Setting up build file system");

            BuildFileSystem = new VirtualFileSystem();
            NetClient.OnClientTagsUpdatedByServer += () =>
            {
                Settings.TagIds = NetClient.TagIds;

                NetClient.RestartConnections();
                SaveSettings();
            };
            NetClient.OnTagListRecieved += (List <Tag> InTags) =>
            {
                TagRenderer.InvalidateResources();
            };
            NetClient.OnPermissionsUpdated += () =>
            {
                BuildFileSystem.ForceRefresh();
                DownloadManager.ForceRefresh();
            };
            NetClient.OnBuildPublished += (string Path, Guid Id) =>
            {
                BuildFileSystem.ForceRefresh();
                DownloadManager.ForceRefresh();
            };
            NetClient.OnBuildUpdated += (string Path, Guid Id) =>
            {
                BuildFileSystem.ForceRefresh();
                DownloadManager.ForceRefresh();
            };
            NetClient.OnConnectedToServer      += () => { BuildFileSystem.ForceRefresh(); };
            NetClient.OnFilteredBuildsRecieved += (Builds) =>
            {
                DownloadManager.RecieveReplicatedBuilds(Builds);
            };
            NetClient.OnBuildsRecieved += (RootPath, Builds) =>
            {
                List <VirtualFileSystemInsertChild> NewChildren = new List <VirtualFileSystemInsertChild>();
                foreach (NetMessage_GetBuildsResponse.BuildInfo Build in Builds)
                {
                    NewChildren.Add(
                        new VirtualFileSystemInsertChild
                    {
                        VirtualPath = Build.VirtualPath,
                        CreateTime  = Build.Guid == Guid.Empty ? DateTime.UtcNow : Build.CreateTime,
                        Metadata    = Build
                    }
                        );
                }

                BuildFileSystem.ReconcileChildren(RootPath, NewChildren);
            };
            BuildFileSystem.OnRequestChildren += (FileSystem, Path) =>
            {
                if (NetClient.IsConnected)
                {
                    NetClient.RequestBuilds(Path);
                }
            };
            BuildFileSystem.Init();

            // Setup download managers for the manifest and app level.
            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Setting up manifest download manager");

            ManifestDownloadManager.Start(
                StorageManager,
                Settings.ManifestDownloadStates,
                BuildRegistry,
                IOQueue
                );

            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Setting up download manager");

            DownloadManager.Start(
                ManifestDownloadManager,
                Settings.DownloadStates,
                BuildFileSystem,
                ScmManager,
                Settings.ReplicationNewerThanTime
                );

            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Setting up update download");

            // Ensure we are downloading the latest update.
            string UpdateDownloadName = "$ Buildsync Update $";

            foreach (DownloadState State in DownloadManager.States.States)
            {
                if (State.Name == UpdateDownloadName)
                {
                    InternalUpdateDownload = State;
                    break;
                }
            }

            if (InternalUpdateDownload == null)
            {
                InternalUpdateDownload = DownloadManager.AddDownload(UpdateDownloadName, "$Internal$/Updates", 2, BuildSelectionRule.Newest, BuildSelectionFilter.None, "", "", true, false, "", "", new List <Guid>(), new List <Guid>());
            }

            // Make sure we have to get the latest manifest id before updating.
            InternalUpdateDownload.ActiveManifestId = Guid.Empty;

            // Clean up any orphan builds.
            StorageManager.CleanUpOrphanBuilds();

            Logger.Log(LogLevel.Info, LogCategory.Main, "OnStart: Complete");
        }
Beispiel #4
0
        /// <summary>
        /// </summary>
        public List <int> Validate(string RootPath, RateTracker Tracker, AsyncIOQueue IOQueue, BuildManfiestValidateProgressCallbackHandler Callback = null)
        {
            LazyCacheBlockInfo();

            List <int> FailedBlocks = new List <int>();

            try
            {
                LockBlockInfo();

                int    TaskCount    = Environment.ProcessorCount;
                Task[] FileTasks    = new Task[TaskCount];
                int    BlockCounter = 0;

                long BytesValidated = 0;
                bool Aborted        = false;

                // Check the size of each file.
                for (int i = 0; i < Files.Count; i++)
                {
                    BuildManifestFileInfo FileInfo = Files[i];
                    string FilePath = Path.Combine(RootPath, FileInfo.Path);
                    string DirPath  = Path.GetDirectoryName(FilePath);

                    if (!Directory.Exists(DirPath))
                    {
                        Directory.CreateDirectory(DirPath);
                        Logger.Log(LogLevel.Warning, LogCategory.Manifest, "Expected directory {0} does not exist, creating.", DirPath);
                    }

                    FileInfo Info = new FileInfo(FilePath);
                    if (!Info.Exists || Info.Length != FileInfo.Size)
                    {
                        using (FileStream Stream = new FileStream(FilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
                        {
                            Logger.Log(LogLevel.Warning, LogCategory.Manifest, "File {0} is not of expected length {1} (is {2}) settting length.", FilePath, FileInfo.Size, Info.Length);
                            Stream.SetLength(FileInfo.Size);
                        }
                    }
                }

                // Check each individual block of data for validity.
                for (int i = 0; i < TaskCount; i++)
                {
                    FileTasks[i] = Task.Run(
                        () =>
                    {
                        byte[] Buffer = new byte[BlockSize];

                        while (!Aborted)
                        {
                            int BlockIndex = Interlocked.Increment(ref BlockCounter) - 1;
                            if (BlockIndex >= BlockCount)
                            {
                                break;
                            }

                            BuildManifestBlockInfo BInfo = BlockInfo[BlockIndex];

                            long BufferLength = 0;
                            bool Success      = GetBlockData(BlockIndex, RootPath, IOQueue, Buffer, out BufferLength);

                            uint Checksum = 0;
                            if (Version >= 2)
                            {
                                Checksum = Crc32Fast.Compute(Buffer, 0, (int)BufferLength);
                            }
                            else
                            {
                                Checksum = Crc32Slow.Compute(Buffer, (int)BufferLength);
                            }

                            if (!Success || BlockChecksums[BlockIndex] != Checksum)
                            {
                                Logger.Log(LogLevel.Warning, LogCategory.Manifest, "Block {0} failed checksum, block contains following sub-blocks:", BlockIndex);

                                for (int SubBlock = 0; SubBlock < BInfo.SubBlocks.Length; SubBlock++)
                                {
                                    BuildManifestSubBlockInfo SubBInfo = BInfo.SubBlocks[SubBlock];
                                    Logger.Log(LogLevel.Warning, LogCategory.Manifest, "\tfile={0} offset={1} size={2}", SubBInfo.File.Path, SubBInfo.FileOffset, SubBInfo.FileSize);
                                }

                                lock (FailedBlocks)
                                {
                                    FailedBlocks.Add(BlockIndex);
                                }
                            }

                            Interlocked.Add(ref BytesValidated, BInfo.TotalSize);

                            if (Callback != null)
                            {
                                if (!Callback.Invoke(BytesValidated, TotalSize, Guid, BlockIndex))
                                {
                                    Aborted = true;
                                }
                            }
                        }
                    }
                        );
                }

                Task.WaitAll(FileTasks);
            }
            finally
            {
                UnlockBlockInfo();
            }

            return(FailedBlocks);
        }
Beispiel #5
0
        /// <summary>
        /// </summary>
        public void InitializeDirectory(string RootPath, AsyncIOQueue IOQueue, bool AllocateFiles, BuildManfiestInitProgressCallbackHandler Callback = null)
        {
            LazyCacheBlockInfo();

            try
            {
                LockBlockInfo();

                const int WriteChunkSize = 16 * 1024 * 1024;
                byte[]    ChunkArray     = new byte[WriteChunkSize];
                byte[]    ChunkPattern   = { 0xDE, 0xAD, 0xBE, 0xEF };

                for (int i = 0; i < WriteChunkSize; i++)
                {
                    ChunkArray[i] = ChunkPattern[i % ChunkPattern.Length];
                }

                long TotalBytes = 0;
                foreach (BuildManifestFileInfo FileInfo in Files)
                {
                    TotalBytes += FileInfo.Size;
                }

                long BytesWritten = 0;
                foreach (BuildManifestFileInfo FileInfo in Files)
                {
                    string FilePath = Path.Combine(RootPath, FileInfo.Path);
                    string FileDir  = Path.GetDirectoryName(FilePath);
                    if (!Directory.Exists(FileDir))
                    {
                        Directory.CreateDirectory(FileDir);
                    }

                    if (AllocateFiles)
                    {
                        using (FileStream Stream = File.OpenWrite(FilePath))
                        {
                            long BytesRemaining = FileInfo.Size;
                            while (BytesRemaining > 0)
                            {
                                long Size = Math.Min(BytesRemaining, WriteChunkSize);
                                Stream.Write(ChunkArray, 0, (int)Size);
                                BytesWritten   += Size;
                                BytesRemaining -= Size;

                                AsyncIOQueue.GlobalBandwidthStats.In(Size);

                                if (Callback != null)
                                {
                                    if (!Callback.Invoke(BytesWritten, TotalBytes))
                                    {
                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            finally
            {
                UnlockBlockInfo();
            }
        }
Beispiel #6
0
        /// <summary>
        /// </summary>
        public static BuildManifest BuildFromDirectory(Guid NewManifestId, string RootPath, string VirtualPath, AsyncIOQueue IOQueue, BuildManfiestProgressCallbackHandler Callback = null)
        {
            string[] FileNames = Directory.GetFiles(RootPath, "*", SearchOption.AllDirectories);
            long     TotalSize = 0;

            // Try and order in the most efficient packing order.
            List <FileInfo> RemainingFiles = new List <FileInfo>();
            List <FileInfo> OrderedList    = new List <FileInfo>();

            for (int i = 0; i < FileNames.Length; i++)
            {
                FileInfo Info = new FileInfo(FileNames[i]);
                RemainingFiles.Add(Info);
                TotalSize += Info.Length;
            }

            // Order main list from largest to smallest.
            RemainingFiles.Sort((Item1, Item2) => - Item1.Length.CompareTo(Item2.Length));

            // Block size.
            long ActualBlockSize = DefaultBlockSize;

            // Try and add in optimal block packing format.
            long BlockIndex = 0;

            while (RemainingFiles.Count > 0)
            {
                FileInfo Info = RemainingFiles[0];
                RemainingFiles.RemoveAt(0);
                OrderedList.Add(Info);
                //Console.WriteLine("Block[{0}] {1}", BlockIndex, Info.Name);

                long BlockCount     = (Info.Length + (ActualBlockSize - 1)) / ActualBlockSize;
                long BytesRemaining = (Info.Length % ActualBlockSize) == 0 ? 0 : ActualBlockSize - Info.Length % ActualBlockSize;
                BlockIndex += BlockCount;

                long SubBlockCount = BlockCount;

                // Try and fit some smaller files into the remaining block space.
                for (int i = 0; i < RemainingFiles.Count && BytesRemaining > 0 && SubBlockCount < MaxSubBlockCount; i++)
                {
                    FileInfo PotentialFile = RemainingFiles[i];
                    if (PotentialFile.Length <= BytesRemaining)
                    {
                        BytesRemaining -= PotentialFile.Length;
                        SubBlockCount++;

                        //Console.WriteLine("\tSubblock[{0}] {1}", BlockIndex, PotentialFile.Name);

                        RemainingFiles.RemoveAt(i);
                        OrderedList.Add(PotentialFile);
                        i--;
                    }
                }
            }

            // Our final list!
            FileInfo[] FileInfos = OrderedList.ToArray();

            BuildManifest Manifest = new BuildManifest();

            Manifest.Guid           = NewManifestId;
            Manifest.VirtualPath    = VirtualPath;
            Manifest.BlockCount     = BlockIndex;
            Manifest.BlockChecksums = new uint[Manifest.BlockCount];
            Manifest.CreateTime     = DateTime.UtcNow;
#if USE_SPARSE_CHECKSUMS
            Manifest.SparseBlockChecksums = new uint[Manifest.BlockCount];
#else
            Manifest.SparseBlockChecksums = null;
#endif
            Manifest.Version   = BuildManifest.CurrentVersion;
            Manifest.BlockSize = ActualBlockSize;

            List <Task> ChecksumTasks = new List <Task>();
            int         FileCounter   = 0;
            int         BlockCounter  = 0;

            for (int i = 0; i < FileInfos.Length; i++)
            {
                Manifest.Files.Add(new BuildManifestFileInfo());
            }

            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                ChecksumTasks.Add(
                    Task.Run(
                        () =>
                {
                    while (true)
                    {
                        int FileIndex = Interlocked.Increment(ref FileCounter) - 1;
                        if (FileIndex >= FileInfos.Length)
                        {
                            break;
                        }

                        FileInfo SubFileInfo = FileInfos[FileIndex];
                        string SubFile       = SubFileInfo.FullName;
                        string RelativePath  = SubFile.Substring(RootPath.Length).Trim('\\', '/');

                        if (Callback != null)
                        {
                            lock (Callback)
                            {
                                float Progress = (FileCounter + BlockCounter) / (float)(FileInfos.Length + Manifest.BlockCount);
                                Callback(RelativePath, Progress * 100);
                            }
                        }

                        BuildManifestFileInfo ManifestFileInfo = new BuildManifestFileInfo();
                        ManifestFileInfo.Path     = RelativePath;
                        ManifestFileInfo.Size     = new FileInfo(SubFile).Length;
                        ManifestFileInfo.Checksum = FileUtils.GetChecksum(SubFile, null);
                        lock (Manifest)
                        {
                            Manifest.Files[FileIndex] = ManifestFileInfo;
                        }
                    }
                }
                        )
                    );
            }

            foreach (Task task in ChecksumTasks)
            {
                task.Wait();
            }

            ChecksumTasks.Clear();

            // Calculate which data goes in eahc block.
            Manifest.CacheBlockInfo();
            Manifest.CacheSizeInfo();
            Manifest.DebugCheck();

            // Calculate checksum for each individual block.
            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                ChecksumTasks.Add(
                    Task.Run(
                        () =>
                {
                    byte[] Buffer = new byte[ActualBlockSize];

                    Stopwatch stopwatch = new Stopwatch();

                    while (true)
                    {
                        int CalculateBlockIndex = Interlocked.Increment(ref BlockCounter) - 1;
                        if (CalculateBlockIndex >= Manifest.BlockCount)
                        {
                            break;
                        }

                        long BufferLength = 0;
                        if (!Manifest.GetBlockData(CalculateBlockIndex, RootPath, IOQueue, Buffer, out BufferLength))
                        {
                            // We should never end up in this situation when publishing ...
                            Debug.Assert(false);
                        }

                        uint Checksum = 0;
                        if (Manifest.Version >= 2)
                        {
                            Checksum = Crc32Fast.Compute(Buffer, 0, (int)BufferLength);
                        }
                        else
                        {
                            Checksum = Crc32Slow.Compute(Buffer, (int)BufferLength);
                        }

                        Manifest.BlockChecksums[CalculateBlockIndex] = Checksum;
#if USE_SPARSE_CHECKSUMS
                        Manifest.SparseBlockChecksums[CalculateBlockIndex] = Crc32Slow.ComputeSparse(Buffer, (int)BufferLength);
#endif

                        if (Callback != null)
                        {
                            lock (Callback)
                            {
                                float Progress = (FileCounter + BlockCounter) / (float)(FileInfos.Length + Manifest.BlockCount);
                                Callback("Checksuming blocks", Progress * 100);
                            }
                        }
                    }
                }
                        )
                    );
            }

            foreach (Task task in ChecksumTasks)
            {
                task.Wait();
            }

            return(Manifest);
        }