private static byte[] GetSha256Hash(PutRequest request) { byte[] hash; using (var sha256 = SHA256.Create()) { hash = sha256.ComputeHash(request.Bytes); } return hash; }
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 } }; }