Example #1
0
 /// <summary>
 /// Creates a new instance of <see cref="Branch"/> (but does
 /// not load it).
 /// </summary>
 /// <param name="store">The command store that this branch is part of (not null)</param>
 /// <param name="ac">Metadata relating to the branch (not null)</param>
 /// <exception cref="ArgumentNullException">
 /// The supplied command store or metadata is undefined</exception>
 internal Branch(CmdStore store, BranchInfo ac)
 {
     Store    = store ?? throw new ArgumentNullException(nameof(store));
     Info     = ac ?? throw new ArgumentNullException(nameof(ac));
     Commands = new List <CmdData>();
     Parent   = null;
     Children = new List <Branch>();
 }
Example #2
0
        /// <summary>
        /// Performs the data processing associated with a command.
        /// </summary>
        public void Process(ExecutionContext context)
        {
            CmdStore cs = context?.Store ?? throw new ApplicationException("Undefined store");

            uint numCmd = (Input as ICreateBranch).CommandCount;

            if (numCmd == 0)
            {
                throw new ApplicationException(nameof(numCmd));
            }

            // Confirm that the name for the new branch is not a
            // duplicate (considering just the children of the
            // current branch)
            string name      = (Input as ICreateBranch).Name;
            Branch parent    = cs.Current;
            Branch oldBranch = parent.GetChild(name);

            if (oldBranch != null)
            {
                throw new ArgumentException(
                          $"Branch {name} previously created at {oldBranch.Info.CreatedAt}");
            }

            // Confirm that the new branch name is acceptable to the command store
            if (!cs.IsValidBranchName(name))
            {
                throw new ArgumentException($"Branch name '{name}' is not allowed");
            }

            var ac = new BranchInfo(
                storeId: cs.Id,
                parentId: parent.Id,
                branchId: Guid.NewGuid(),
                branchName: name,
                createdAt: Input.CreatedAt,
                updatedAt: Input.CreatedAt,
                commandDiscount: 1,
                refreshCount: numCmd);

            // Update internal structure to include the new branch
            var newBranch = new Branch(cs, ac)
            {
                Parent = parent
            };

            parent.Children.Add(newBranch);
            cs.Branches.Add(ac.BranchId, newBranch);

            // Save the metadata for the new branch
            cs.SaveBranchInfo(newBranch);

            // And save the CreateBranch command itself
            newBranch.SaveData(Input);
        }
        /// <summary>
        /// Performs the data processing associated with a command.
        /// </summary>
        /// <remarks>If a call to this method completes without any exception,
        /// a reference to the new store can be obtained using the
        /// <see cref="ExecutionContext.Store"/> property.
        /// </remarks>
        public void Process(ExecutionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // Create the physical manifestation (if any) for the new store
            var store = CmdStore.CreateStore(Input);

            // Write the command data
            store.Current.SaveData(Input);
            context.Store = store;
        }
Example #4
0
        public void Process(ExecutionContext context)
        {
            CmdStore  cs        = context.Store;
            StoreInfo storeInfo = cs.Store;
            string    upLoc     = storeInfo.UpstreamLocation;

            if (String.IsNullOrEmpty(upLoc))
            {
                throw new ApplicationException("There is no upstream location");
            }

            // Collate the number of commands we have in local branches
            IdCount[] have = cs.Branches.Values
                             .Where(x => !x.IsRemote)
                             .Select(x => new IdCount(x.Id, x.Info.CommandCount))
                             .ToArray();

            IRemoteStore rs = context.GetRemoteStore(upLoc);

            // We don't care about new branches in the origin, but we do care
            // about local branches that have been created since the last push
            IdRange[] toPush = rs.GetMissingRanges(cs.Id, have, false).ToArray();

            // How many commands do we need to push
            uint total = (uint)toPush.Sum(x => x.Size);

            Log.Info($"To push {total} command`s in {toPush.Length} branch`es".TrimExtras());

            foreach (IdRange idr in toPush)
            {
                Branch b = cs.FindBranch(idr.Id);
                if (b == null)
                {
                    throw new ApplicationException("Cannot locate branch " + idr.Id);
                }

                Log.Info($"Push [{idr.Min},{idr.Max}] from {b}");

                CmdData[] data = b.TakeRange(idr.Min, idr.Max).ToArray();
                rs.Push(cs.Name, b.Info, data);

                // Remember how much we were ahead at the time of the push
                b.Info.LastPush = idr.Max + 1;
                b.Store.SaveBranchInfo(b);
            }

            Log.Info("Push completed");
        }
        public void Process(ExecutionContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // Create the new store
            var store = CmdStore.CreateStore(Input);

            // Write the command data
            // TODO: Does it need to be written anywhere? Perhaps to root metadata?
            // We shouldn't really touch any of the branches that have just been copied.
            //store.Current.SaveData(Input);
            context.Store = store;
        }
Example #6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ExecutionContext"/> class.
 /// </summary>
 /// <param name="store">The command store (not null).</param>
 /// <exception cref="ArgumentNullException">
 /// The specified store is undefined.</exception>
 public ExecutionContext(CmdStore store)
     : this()
 {
     Store = store ?? throw new ArgumentNullException();
 }
Example #7
0
        public void Process(ExecutionContext context)
        {
            CmdStore  cs        = context.Store;
            StoreInfo storeInfo = cs.Store;
            string    upLoc     = storeInfo.UpstreamLocation;

            if (String.IsNullOrEmpty(upLoc))
            {
                throw new ApplicationException("There is no upstream location");
            }

            // Collate how much we already have in all remote branches.
            // We may have previously pushed some local branches to the remote
            // store, but nothing is supposed to mutate those remote copies.

            IdCount[] have = cs.Branches.Values
                             .Where(x => x.IsRemote)
                             .Select(x => new IdCount(x.Id, x.Info.CommandCount))
                             .ToArray();

            // Open a channel to the upstream store
            IRemoteStore rs = context.GetRemoteStore(upLoc);

            // Determine what we are missing (including any new branches in the remote)
            IdRange[] toFetch = rs.GetMissingRanges(cs.Id, have, true);

            // How many commands do we need to fetch
            uint total = (uint)toFetch.Sum(x => x.Size);

            Log.Info($"To fetch {total} command`s from {toFetch.Length} branch`es".TrimExtras());

            // Retrieve the command data from the remote, keeping new branches
            // apart from appends to existing branches.

            var newBranchData  = new Dictionary <BranchInfo, CmdData[]>();
            var moreBranchData = new Dictionary <BranchInfo, CmdData[]>();

            foreach (IdRange idr in toFetch)
            {
                // Fetch the remote AC file
                BranchInfo ac = rs.GetBranchInfo(idr.Id);
                if (ac == null)
                {
                    throw new ApplicationException("Could not locate remote branch " + idr.Id);
                }

                // And the relevant data
                Log.Info($"Fetch [{idr.Min},{idr.Max}] for {ac.BranchName} ({ac.BranchId})");
                CmdData[] branchData = rs.GetData(idr).ToArray();

                if (cs.FindBranch(ac.BranchId) == null)
                {
                    newBranchData.Add(ac, branchData);
                }
                else
                {
                    moreBranchData.Add(ac, branchData);
                }
            }

            // All done with the remote store

            // Copy any brand new branches (ensuring they get created in the
            // right order so that parent/child relationships can be formed
            // as we go).

            foreach (KeyValuePair <BranchInfo, CmdData[]> kvp in newBranchData.OrderBy(x => x.Key.CreatedAt))
            {
                cs.CopyIn(kvp.Key, kvp.Value);
            }

            // Append command data for branches we previously had (the order
            // shouldn't matter)

            foreach (KeyValuePair <BranchInfo, CmdData[]> kvp in moreBranchData)
            {
                cs.CopyIn(kvp.Key, kvp.Value);
            }

            Log.Info("Fetch completed");

            // Reload the current command stream (from scratch, kind of brute force,
            // not sure whether appending the new commands would really be sufficient)
            // TODO: Is this really necessary? Perhaps only if the current branch has
            // been fetched (the stuff we're fetching should only come from remotes,
            // but the current branch could be one of those remotes)
            //cs.Stream = new CmdStream(cs.Current);
        }
Example #8
0
        public void Process(ExecutionContext context)
        {
            Guid     sourceId = (Input as IMerge).FromId;
            CmdStore cs       = context.Store;
            Branch   target   = cs.Current;
            Branch   source   = cs.FindBranch(sourceId);

            if (source == null)
            {
                throw new ApplicationException($"Cannot locate branch {sourceId}");
            }

            // Merges can only be done between branches that are part
            // of the local store (fetches from remote stores should
            // involve a totally different set of branches)
            //if (target.Info.StoreId != source.Info.StoreId)
            //    throw new NotSupportedException("Attempt to merge with a remote store");

            // Determine the first command that needs to be merged from
            // the source branch. The source branch doesn't know anything
            // about merges that have already been done, so figuring this
            // out involves scanning back from the end of the target branch
            // to locate the last merge (if any).

            // How far we go back depends on whether we're merging from
            // a child branch to its parent, or vice versa. If the target
            // is the child we go back as far as command 0. If the target
            // is the parent, we go back as far the command that followed
            // the branch start point.

            bool targetIsParent = target.Id.Equals(source.Info.ParentId);

            // Define the range of commands to be merged from the source

            uint minCmd = 0;

            if (targetIsParent)
            {
                // Merging into the parent, so start from the command immediately
                // after the last command that was previously merged (or 0 if this
                // is the first time the parent has merged from the child).

                if (target.Info.LastMerge.TryGetValue(sourceId, out MergeInfo mi))
                {
                    minCmd = mi.ChildCount;
                }
            }
            else
            {
                // Merging from the parent into the child, so start from the command
                // immediately after the last command that was previously merged (it
                // could potentially be 0)

                minCmd = target.Info.RefreshCount;
            }

            uint maxCmd = (uint)source.Info.CommandCount - 1;

            // TODO: Round about here we need to actually include the new stuff
            // as part of the current stream. Replaying the commands may then
            // lead to some sort of conflict (things are not guaranteed to work),
            // so there needs to be some way to preview the results.

            // Write the command data
            Input.Add(nameof(IMerge.MinCmd), minCmd);
            Input.Add(nameof(IMerge.MaxCmd), maxCmd);
            target.SaveData(Input);
        }