private static byte[] GetSha256Hash(PutRequest request)
 {
     byte[] hash;
     using (var sha256 = SHA256.Create())
     {
         hash = sha256.ComputeHash(request.Bytes);
     }
     return hash;
 }
Ejemplo n.º 2
0
        private PutResult[] PutInternal(PutRequest[] valuesToAdd, int backupIndex)
        {
            var results = new PutResult[valuesToAdd.Length];

            var groupedByEndpoint = from req in valuesToAdd
                                    let er = new
                                    {
                                        OriginalIndex = Array.IndexOf(valuesToAdd, req),
                                        Put = new ExtendedPutRequest
                                        {
                                            Bytes = req.Bytes,
                                            ExpiresAt = req.ExpiresAt,
                                            IsReadOnly = req.IsReadOnly,
                                            Key = req.Key,
                                            OptimisticConcurrency = req.OptimisticConcurrency,
                                            ParentVersions = req.ParentVersions,
                                            Segment = GetSegmentFromKey(req.Key),
                                        }
                                    }
                                    group er by GetEndpointByBackupIndex(topology.Segments[er.Put.Segment], backupIndex) into g
                                    select g;

            foreach (var endpoint in groupedByEndpoint)
            {
                if (endpoint.Key == null)
                    throw new NoMoreBackupsException();

                var requests = endpoint.ToArray();
                var putRequests = requests.Select(x => x.Put).ToArray();

                var putsResults = GetPutsResults(endpoint.Key, putRequests, backupIndex);
                for (var i = 0; i < putsResults.Length; i++)
                {
                    results[requests[i].OriginalIndex] = putsResults[i];
                }
            }
            return results;
        }
        public PutResult Put(PutRequest request)
        {
            var hash = GetSha256Hash(request);
            var versions = GatherActiveVersions(request.Key);
            if (versions.Length == 1)
            {
                // Test that the key doesn't already exist with matching value
                var values = Get(new GetRequest { Key = request.Key, SpecifiedVersion = versions[0] });
                if (values.Length == 1 && values[0].Sha256Hash.SequenceEqual(hash))
                {
                    return new PutResult
                    {
                        ConflictExists = false,
                        Version = versions[0]
                    };
                }
            }

            // we always accept read only put requests, because
            // we assume that there can never be a conflict with them
            // since they are read only values.
            var doesAllVersionsMatch =
                request.IsReadOnly ?
                request.IsReadOnly :
                DoesAllVersionsMatch(request.Key, request.ParentVersions);

            if (doesAllVersionsMatch == false &&
                request.OptimisticConcurrency)
            {
                return new PutResult
                {
                    ConflictExists = true,
                    Version = null
                };
            }

            // always remove the active versions from the cache
            commitSyncronization.Add(() => cache.Remove(GetKey(request.Key)));
            if (doesAllVersionsMatch)
            {
                // we only remove existing versions from the
                // cache if we delete them from the database
                foreach (var parentVersion in versions)
                {
                    var copy = parentVersion;
                    commitSyncronization.Add(() => cache.Remove(GetKey(request.Key, copy)));
                }
                DeleteAllKeyValuesForVersions(request.Key, versions);
            }

            var instanceIdForRow = instanceId;
            if (request.ReplicationVersion != null)
                instanceIdForRow = request.ReplicationVersion.InstanceId;

            int versionNumber = request.ReplicationVersion == null ?
                    GenerateVersionNumber() :
                    request.ReplicationVersion.Number;
            using (var update = new Update(session, keys, JET_prep.Insert))
            {
                Api.SetColumn(session, keys, keysColumns["key"], request.Key, Encoding.Unicode);

                Api.SetColumn(session, keys, keysColumns["version_instance_id"], instanceIdForRow.ToByteArray());
                Api.SetColumn(session, keys, keysColumns["version_number"], versionNumber);

                if (request.ExpiresAt.HasValue)
                    Api.SetColumn(session, keys, keysColumns["expiresAt"], request.ExpiresAt.Value.ToOADate());

                update.Save();
            }

            using (var update = new Update(session, data, JET_prep.Insert))
            {
                Api.SetColumn(session, data, dataColumns["key"], request.Key, Encoding.Unicode);
                Api.SetColumn(session, data, dataColumns["version_number"], versionNumber);
                Api.SetColumn(session, data, dataColumns["version_instance_id"], instanceIdForRow.ToByteArray());
                Api.SetColumn(session, data, dataColumns["data"], request.Bytes);
                Api.SetColumn(session, data, dataColumns["sha256_hash"], hash);
                Api.SetColumn(session, data, dataColumns["readonly"], request.IsReadOnly);

                var timestamp = DateTime.Now.ToOADate();
                if (request.ReplicationTimeStamp.HasValue)
                    timestamp = request.ReplicationTimeStamp.Value.ToOADate();
                Api.SetColumn(session, data, dataColumns["timestamp"], timestamp);

                if (request.ExpiresAt.HasValue)
                    Api.SetColumn(session, data, dataColumns["expiresAt"], request.ExpiresAt.Value.ToOADate());

                WriteAllParentVersions(request.ParentVersions);

                update.Save();
            }

            return new PutResult
            {
                ConflictExists = doesAllVersionsMatch == false,
                Version = new ValueVersion
                {
                    InstanceId = instanceIdForRow,
                    Number = versionNumber
                }
            };
        }