PageRenderTime 55ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 1ms

/Raven.Client.Lightweight/Connection/Async/AsyncServerClient.cs

http://github.com/ayende/ravendb
C# | 2727 lines | 2322 code | 386 blank | 19 comment | 323 complexity | 2c9f9bc948dccdd45e455eeb26ab532d MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0

Large files files are truncated, but you can click here to view the full file

  1. //-----------------------------------------------------------------------
  2. // <copyright file="AsyncServerClient.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using Raven.Abstractions;
  7. using Raven.Abstractions.Cluster;
  8. using Raven.Abstractions.Commands;
  9. using Raven.Abstractions.Connection;
  10. using Raven.Abstractions.Data;
  11. using Raven.Abstractions.Exceptions;
  12. using Raven.Abstractions.Extensions;
  13. using Raven.Abstractions.Indexing;
  14. using Raven.Abstractions.Json;
  15. using Raven.Abstractions.Replication;
  16. using Raven.Abstractions.Smuggler;
  17. using Raven.Abstractions.Util;
  18. using Raven.Client.Changes;
  19. using Raven.Client.Connection.Implementation;
  20. using Raven.Client.Connection.Profiling;
  21. using Raven.Client.Connection.Request;
  22. using Raven.Client.Document;
  23. using Raven.Client.Exceptions;
  24. using Raven.Client.Extensions;
  25. using Raven.Client.Indexes;
  26. using Raven.Client.Listeners;
  27. using Raven.Client.Metrics;
  28. using Raven.Client.Util.Auth;
  29. using Raven.Database.Data;
  30. using Raven.Imports.Newtonsoft.Json;
  31. using Raven.Imports.Newtonsoft.Json.Linq;
  32. using Raven.Json.Linq;
  33. using System;
  34. using System.Collections.Generic;
  35. using System.Collections.Specialized;
  36. using System.Globalization;
  37. using System.IO;
  38. using System.Linq;
  39. using System.Net;
  40. using System.Net.Http;
  41. using System.Text;
  42. using System.Threading;
  43. using System.Threading.Tasks;
  44. namespace Raven.Client.Connection.Async
  45. {
  46. public class AsyncServerClient : IAsyncDatabaseCommands, IAsyncInfoDatabaseCommands
  47. {
  48. private readonly ProfilingInformation profilingInformation;
  49. private readonly IDocumentConflictListener[] conflictListeners;
  50. private readonly Guid? sessionId;
  51. private readonly Func<AsyncServerClient, string, bool, IRequestExecuter> requestExecuterGetter;
  52. private readonly Func<string, RequestTimeMetric> requestTimeMetricGetter;
  53. private readonly string databaseName;
  54. private readonly string primaryUrl;
  55. private readonly OperationCredentials credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication;
  56. private readonly RequestExecuterSelector requestExecuterSelector;
  57. internal readonly DocumentConvention convention;
  58. internal readonly HttpJsonRequestFactory jsonRequestFactory;
  59. private int requestCount;
  60. private NameValueCollection operationsHeaders = new NameValueCollection();
  61. public string Url
  62. {
  63. get { return primaryUrl; }
  64. }
  65. public IRequestExecuter RequestExecuter
  66. {
  67. get { return requestExecuterSelector.Select(); }
  68. }
  69. public OperationCredentials PrimaryCredentials
  70. {
  71. get
  72. {
  73. return credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication;
  74. }
  75. }
  76. public AsyncServerClient(
  77. string url,
  78. DocumentConvention convention,
  79. OperationCredentials credentials,
  80. HttpJsonRequestFactory jsonRequestFactory,
  81. Guid? sessionId,
  82. Func<AsyncServerClient, string, bool, IRequestExecuter> requestExecuterGetter,
  83. Func<string, RequestTimeMetric> requestTimeMetricGetter,
  84. string databaseName,
  85. IDocumentConflictListener[] conflictListeners,
  86. bool incrementReadStripe)
  87. {
  88. profilingInformation = ProfilingInformation.CreateProfilingInformation(sessionId);
  89. primaryUrl = url;
  90. if (primaryUrl.EndsWith("/"))
  91. primaryUrl = primaryUrl.Substring(0, primaryUrl.Length - 1);
  92. this.jsonRequestFactory = jsonRequestFactory;
  93. this.sessionId = sessionId;
  94. this.convention = convention;
  95. credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication = credentials;
  96. this.databaseName = databaseName;
  97. this.conflictListeners = conflictListeners;
  98. this.requestExecuterGetter = requestExecuterGetter;
  99. this.requestTimeMetricGetter = requestTimeMetricGetter;
  100. requestExecuterSelector = new RequestExecuterSelector(() =>
  101. requestExecuterGetter(this, databaseName, incrementReadStripe), convention);
  102. requestExecuterSelector.Select().UpdateReplicationInformationIfNeededAsync(this);
  103. }
  104. public void Dispose()
  105. {
  106. }
  107. public Task<string[]> GetIndexNamesAsync(int start, int pageSize, CancellationToken token = default(CancellationToken))
  108. {
  109. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  110. {
  111. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.IndexNames(start, pageSize), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  112. {
  113. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  114. return json.Select(x => x.Value<string>()).ToArray();
  115. }
  116. }, token);
  117. }
  118. public Task<IndexDefinition[]> GetIndexesAsync(int start, int pageSize, CancellationToken token = default(CancellationToken))
  119. {
  120. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  121. {
  122. var operationUrl = operationMetadata.Url + "/indexes/?start=" + start + "&pageSize=" + pageSize;
  123. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)))
  124. {
  125. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  126. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  127. //NOTE: To review, I'm not confidence this is the correct way to deserialize the index definition
  128. return json.Select(x =>
  129. {
  130. var value = ((RavenJObject)x)["definition"].ToString();
  131. return JsonConvert.DeserializeObject<IndexDefinition>(value, new JsonToJsonConverter());
  132. }).ToArray();
  133. }
  134. }, token);
  135. }
  136. public Task<TransformerDefinition[]> GetTransformersAsync(int start, int pageSize, CancellationToken token = default(CancellationToken))
  137. {
  138. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  139. {
  140. var operationUrl = operationMetadata.Url + "/transformers?start=" + start + "&pageSize=" + pageSize;
  141. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)))
  142. {
  143. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  144. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  145. //NOTE: To review, I'm not confidence this is the correct way to deserialize the transformer definition
  146. return json.Select(x => JsonConvert.DeserializeObject<TransformerDefinition>(((RavenJObject)x)["definition"].ToString(), new JsonToJsonConverter())).ToArray();
  147. }
  148. }, token);
  149. }
  150. public Task SetTransformerLockAsync(string name, TransformerLockMode lockMode, CancellationToken token = default(CancellationToken))
  151. {
  152. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  153. {
  154. var operationUrl = operationMetadata.Url + "/transformers/" + name + "?op=" + "lockModeChange" + "&mode=" + lockMode;
  155. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Post, operationMetadata.Credentials, convention)))
  156. {
  157. request.AddOperationHeaders(OperationsHeaders);
  158. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  159. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  160. }
  161. }, token);
  162. }
  163. internal Task ReplicateIndexAsync(string name, CancellationToken token = default(CancellationToken))
  164. {
  165. var url = String.Format("/replication/replicate-indexes?indexName={0}", Uri.EscapeDataString(name));
  166. using (var request = CreateRequest(url, HttpMethods.Post))
  167. return request.ExecuteRawResponseAsync();
  168. }
  169. public Task ResetIndexAsync(string name, CancellationToken token = default(CancellationToken))
  170. {
  171. return ExecuteWithReplication(HttpMethods.Reset, async (operationMetadata, requestTimeMetric) =>
  172. {
  173. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/indexes/" + name, HttpMethods.Reset, operationMetadata.Credentials, convention, requestTimeMetric)))
  174. {
  175. request.AddOperationHeaders(OperationsHeaders);
  176. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  177. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  178. }
  179. }, token);
  180. }
  181. public Task SetIndexLockAsync(string name, IndexLockMode unLockMode, CancellationToken token = default(CancellationToken))
  182. {
  183. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  184. {
  185. var operationUrl = operationMetadata.Url + "/indexes/" + name + "?op=" + "lockModeChange" + "&mode=" + unLockMode;
  186. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric)))
  187. {
  188. request.AddOperationHeaders(OperationsHeaders);
  189. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  190. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  191. }
  192. }, token);
  193. }
  194. public Task SetIndexPriorityAsync(string name, IndexingPriority priority, CancellationToken token = default(CancellationToken))
  195. {
  196. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  197. {
  198. var operationUrl = operationMetadata.Url + "/indexes/set-priority/" + name + "?priority=" + priority;
  199. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric)))
  200. {
  201. request.AddOperationHeaders(OperationsHeaders);
  202. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  203. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  204. }
  205. }, token);
  206. }
  207. public Task<string> PutIndexAsync<TDocument, TReduceResult>(string name, IndexDefinitionBuilder<TDocument, TReduceResult> indexDef, CancellationToken token = default(CancellationToken))
  208. {
  209. return PutIndexAsync(name, indexDef, false, token);
  210. }
  211. public Task<string> PutIndexAsync<TDocument, TReduceResult>(string name,
  212. IndexDefinitionBuilder<TDocument, TReduceResult> indexDef, bool overwrite = false, CancellationToken token = default(CancellationToken))
  213. {
  214. return PutIndexAsync(name, indexDef.ToIndexDefinition(convention), overwrite, token);
  215. }
  216. public Task<bool> IndexHasChangedAsync(string name, IndexDefinition indexDef, CancellationToken token = default(CancellationToken))
  217. {
  218. return ExecuteWithReplication(HttpMethod.Post, (operationMetadata, requestTimeMetric) => DirectIndexHasChangedAsync(name, indexDef, operationMetadata, requestTimeMetric, token), token);
  219. }
  220. private async Task<bool> DirectIndexHasChangedAsync(string name, IndexDefinition indexDef, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token)
  221. {
  222. var requestUri = operationMetadata.Url.Indexes(name) + "?op=hasChanged";
  223. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  224. {
  225. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  226. var serializeObject = JsonConvert.SerializeObject(indexDef, Default.Converters);
  227. await request.WriteAsync(serializeObject).WithCancellation(token).ConfigureAwait(false);
  228. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  229. return result.Value<bool>("Changed");
  230. }
  231. }
  232. public Task<string> PutIndexAsync(string name, IndexDefinition indexDef, CancellationToken token = default(CancellationToken))
  233. {
  234. return PutIndexAsync(name, indexDef, false, token);
  235. }
  236. public Task<string> PutIndexAsync(string name, IndexDefinition indexDef, bool overwrite, CancellationToken token = default(CancellationToken))
  237. {
  238. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutIndexAsync(name, indexDef, overwrite, operationMetadata, requestTimeMetric, token), token);
  239. }
  240. public Task<string[]> PutIndexesAsync(IndexToAdd[] indexesToAdd, CancellationToken token = default(CancellationToken))
  241. {
  242. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutIndexesAsync(indexesToAdd, operationMetadata, token), token);
  243. }
  244. public Task<string[]> PutSideBySideIndexesAsync(IndexToAdd[] indexesToAdd, Etag minimumEtagBeforeReplace = null, DateTime? replaceTimeUtc = null, CancellationToken token = default(CancellationToken))
  245. {
  246. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutSideBySideIndexesAsync(indexesToAdd, operationMetadata, minimumEtagBeforeReplace, replaceTimeUtc, token), token);
  247. }
  248. public Task<string> PutTransformerAsync(string name, TransformerDefinition transformerDefinition, CancellationToken token = default(CancellationToken))
  249. {
  250. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutTransformerAsync(name, transformerDefinition, operationMetadata, requestTimeMetric, token), token);
  251. }
  252. public async Task<string> DirectPutIndexAsync(string name, IndexDefinition indexDef, bool overwrite, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  253. {
  254. var requestUri = operationMetadata.Url + "/indexes/" + Uri.EscapeUriString(name) + "?definition=yes";
  255. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  256. {
  257. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  258. try
  259. {
  260. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  261. if (overwrite == false)
  262. throw new InvalidOperationException("Cannot put index: " + name + ", index already exists");
  263. }
  264. catch (ErrorResponseException e)
  265. {
  266. if (e.StatusCode != HttpStatusCode.NotFound)
  267. throw;
  268. }
  269. }
  270. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Put, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  271. {
  272. var serializeObject = JsonConvert.SerializeObject(indexDef, Default.Converters);
  273. ErrorResponseException responseException;
  274. try
  275. {
  276. await request.WriteAsync(serializeObject).ConfigureAwait(false);
  277. var result = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  278. return result.Value<string>("Index");
  279. }
  280. catch (ErrorResponseException e)
  281. {
  282. if (e.StatusCode != HttpStatusCode.BadRequest) throw;
  283. responseException = e;
  284. }
  285. var error = await responseException.TryReadErrorResponseObject(new { Error = "", Message = "", IndexDefinitionProperty = "", ProblematicText = "" }).ConfigureAwait(false);
  286. if (error == null) throw responseException;
  287. throw new IndexCompilationException(error.Message) { IndexDefinitionProperty = error.IndexDefinitionProperty, ProblematicText = error.ProblematicText };
  288. }
  289. }
  290. public async Task<string[]> DirectPutIndexesAsync(IndexToAdd[] indexesToAdd, OperationMetadata operationMetadata, CancellationToken token = default(CancellationToken))
  291. {
  292. var requestUri = operationMetadata.Url + "/indexes";
  293. return await PutIndexes(operationMetadata, token, requestUri, indexesToAdd).ConfigureAwait(false);
  294. }
  295. public async Task<string[]> DirectPutSideBySideIndexesAsync(IndexToAdd[] indexesToAdd, OperationMetadata operationMetadata, Etag minimumEtagBeforeReplace, DateTime? replaceTimeUtc, CancellationToken token = default(CancellationToken))
  296. {
  297. var sideBySideIndexes = new SideBySideIndexes
  298. {
  299. IndexesToAdd = indexesToAdd,
  300. MinimumEtagBeforeReplace = minimumEtagBeforeReplace,
  301. ReplaceTimeUtc = replaceTimeUtc
  302. };
  303. var requestUri = operationMetadata.Url + "/side-by-side-indexes";
  304. return await PutIndexes(operationMetadata, token, requestUri, sideBySideIndexes).ConfigureAwait(false);
  305. }
  306. private async Task<string[]> PutIndexes(OperationMetadata operationMetadata, CancellationToken token, string requestUri, object obj)
  307. {
  308. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Put, operationMetadata.Credentials, convention).AddOperationHeaders(OperationsHeaders)))
  309. {
  310. var serializeObject = JsonConvert.SerializeObject(obj, Default.Converters);
  311. ErrorResponseException responseException;
  312. try
  313. {
  314. await request.WriteAsync(serializeObject).WithCancellation(token).ConfigureAwait(false);
  315. var result = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  316. return result
  317. .Value<RavenJArray>("Indexes")
  318. .Select(x => x.Value<string>())
  319. .ToArray();
  320. }
  321. catch (ErrorResponseException e)
  322. {
  323. if (e.StatusCode != HttpStatusCode.BadRequest)
  324. throw;
  325. responseException = e;
  326. }
  327. var error = await responseException.TryReadErrorResponseObject(new { Error = "", Message = "", IndexDefinitionProperty = "", ProblematicText = "" }).ConfigureAwait(false);
  328. if (error == null)
  329. throw responseException;
  330. throw new IndexCompilationException(error.Message) { IndexDefinitionProperty = error.IndexDefinitionProperty, ProblematicText = error.ProblematicText };
  331. }
  332. }
  333. public async Task<string> DirectPutTransformerAsync(string name, TransformerDefinition transformerDefinition, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  334. {
  335. var requestUri = operationMetadata.Url + "/transformers/" + name;
  336. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Put, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  337. {
  338. var serializeObject = JsonConvert.SerializeObject(transformerDefinition, Default.Converters);
  339. ErrorResponseException responseException;
  340. try
  341. {
  342. await request.WriteAsync(serializeObject).ConfigureAwait(false);
  343. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  344. return result.Value<string>("Transformer");
  345. }
  346. catch (BadRequestException e)
  347. {
  348. throw new TransformCompilationException(e.Message);
  349. }
  350. catch (ErrorResponseException e)
  351. {
  352. if (e.StatusCode != HttpStatusCode.BadRequest)
  353. throw;
  354. responseException = e;
  355. }
  356. var error = await responseException.TryReadErrorResponseObject(new { Error = "", Message = "" }).ConfigureAwait(false);
  357. if (error == null)
  358. throw responseException;
  359. throw new TransformCompilationException(error.Message);
  360. }
  361. }
  362. public Task DeleteIndexAsync(string name, CancellationToken token = default(CancellationToken))
  363. {
  364. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  365. {
  366. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Indexes(name), HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(operationsHeaders)))
  367. {
  368. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  369. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  370. }
  371. }, token);
  372. }
  373. public Task<Operation> DeleteByIndexAsync(string indexName, IndexQuery queryToDelete, BulkOperationOptions options = null, CancellationToken token = default(CancellationToken))
  374. {
  375. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  376. {
  377. var notNullOptions = options ?? new BulkOperationOptions();
  378. string path = queryToDelete.GetIndexQueryUrl(operationMetadata.Url, indexName, "bulk_docs") + "&allowStale=" + notNullOptions.AllowStale
  379. + "&details=" + notNullOptions.RetrieveDetails;
  380. if (notNullOptions.MaxOpsPerSec != null)
  381. path += "&maxOpsPerSec=" + notNullOptions.MaxOpsPerSec;
  382. if (notNullOptions.StaleTimeout != null)
  383. path += "&staleTimeout=" + notNullOptions.StaleTimeout;
  384. token.ThrowCancellationIfNotDefault(); //maybe the operation is canceled and we can spare the request..
  385. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  386. {
  387. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  388. RavenJToken jsonResponse;
  389. try
  390. {
  391. jsonResponse = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  392. }
  393. catch (ErrorResponseException e)
  394. {
  395. if (e.StatusCode == HttpStatusCode.NotFound) throw new InvalidOperationException("There is no index named: " + indexName, e);
  396. throw;
  397. }
  398. // Be compatible with the response from v2.0 server
  399. var opId = ((RavenJObject)jsonResponse)["OperationId"];
  400. if (opId == null || opId.Type != JTokenType.Integer) return null;
  401. return new Operation(this, opId.Value<long>());
  402. }
  403. }, token);
  404. }
  405. public Task DeleteTransformerAsync(string name, CancellationToken token = default(CancellationToken))
  406. {
  407. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  408. {
  409. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Transformer(name), HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(operationsHeaders)))
  410. {
  411. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  412. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  413. }
  414. }, token);
  415. }
  416. public Task<RavenJObject> PatchAsync(string key, PatchRequest[] patches, CancellationToken token = default(CancellationToken))
  417. {
  418. return PatchAsync(key, patches, null, token);
  419. }
  420. public async Task<RavenJObject> PatchAsync(string key, PatchRequest[] patches, bool ignoreMissing, CancellationToken token = default(CancellationToken))
  421. {
  422. var batchResults = await BatchAsync(new ICommandData[]
  423. {
  424. new PatchCommandData
  425. {
  426. Key = key,
  427. Patches = patches,
  428. }
  429. }, null, token).ConfigureAwait(false);
  430. if (!ignoreMissing && batchResults[0].PatchResult != null &&
  431. batchResults[0].PatchResult == PatchResult.DocumentDoesNotExists)
  432. throw new DocumentDoesNotExistsException("Document with key " + key + " does not exist.");
  433. return batchResults[0].AdditionalData;
  434. }
  435. public Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patch, CancellationToken token = default(CancellationToken))
  436. {
  437. return PatchAsync(key, patch, null, token);
  438. }
  439. public async Task<RavenJObject> PatchAsync(string key, PatchRequest[] patches, Etag etag, CancellationToken token = default(CancellationToken))
  440. {
  441. var batchResults = await BatchAsync(new ICommandData[]
  442. {
  443. new PatchCommandData
  444. {
  445. Key = key,
  446. Patches = patches,
  447. Etag = etag,
  448. }
  449. }, null, token).ConfigureAwait(false);
  450. return batchResults[0].AdditionalData;
  451. }
  452. public async Task<RavenJObject> PatchAsync(string key, PatchRequest[] patchesToExisting,
  453. PatchRequest[] patchesToDefault, RavenJObject defaultMetadata, CancellationToken token = default(CancellationToken))
  454. {
  455. var batchResults = await BatchAsync(new ICommandData[]
  456. {
  457. new PatchCommandData
  458. {
  459. Key = key,
  460. Patches = patchesToExisting,
  461. PatchesIfMissing = patchesToDefault,
  462. Metadata = defaultMetadata
  463. }
  464. }, null, token).ConfigureAwait(false);
  465. return batchResults[0].AdditionalData;
  466. }
  467. public async Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patch, bool ignoreMissing, CancellationToken token = default(CancellationToken))
  468. {
  469. var batchResults = await BatchAsync(new ICommandData[]
  470. {
  471. new ScriptedPatchCommandData
  472. {
  473. Key = key,
  474. Patch = patch,
  475. }
  476. }, null, token).ConfigureAwait(false);
  477. if (!ignoreMissing && batchResults[0].PatchResult != null &&
  478. batchResults[0].PatchResult == PatchResult.DocumentDoesNotExists)
  479. throw new DocumentDoesNotExistsException("Document with key " + key + " does not exist.");
  480. return batchResults[0].AdditionalData;
  481. }
  482. public async Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patch, Etag etag, CancellationToken token = default(CancellationToken))
  483. {
  484. var batchResults = await BatchAsync(new ICommandData[]
  485. {
  486. new ScriptedPatchCommandData
  487. {
  488. Key = key,
  489. Patch = patch,
  490. Etag = etag
  491. }
  492. }, null, token).ConfigureAwait(false);
  493. return batchResults[0].AdditionalData;
  494. }
  495. public async Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patchExisting,
  496. ScriptedPatchRequest patchDefault, RavenJObject defaultMetadata, CancellationToken token = default(CancellationToken))
  497. {
  498. var batchResults = await BatchAsync(new ICommandData[]
  499. {
  500. new ScriptedPatchCommandData
  501. {
  502. Key = key,
  503. Patch = patchExisting,
  504. PatchIfMissing = patchDefault,
  505. Metadata = defaultMetadata
  506. }
  507. }, null, token).ConfigureAwait(false);
  508. return batchResults[0].AdditionalData;
  509. }
  510. public Task<PutResult> PutAsync(string key, Etag etag, RavenJObject document, RavenJObject metadata, CancellationToken token = default(CancellationToken))
  511. {
  512. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutAsync(operationMetadata, requestTimeMetric, key, etag, document, metadata, token), token);
  513. }
  514. private async Task<PutResult> DirectPutAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, Etag etag, RavenJObject document, RavenJObject metadata, CancellationToken token = default(CancellationToken))
  515. {
  516. if (metadata == null)
  517. metadata = new RavenJObject();
  518. var method = String.IsNullOrEmpty(key) ? HttpMethod.Post : HttpMethod.Put;
  519. if (etag != null)
  520. metadata[Constants.MetadataEtagField] = new RavenJValue((string)etag);
  521. else
  522. metadata.Remove(Constants.MetadataEtagField);
  523. if (key != null)
  524. key = Uri.EscapeDataString(key);
  525. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/docs/" + key, method, metadata, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  526. {
  527. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  528. ErrorResponseException responseException;
  529. try
  530. {
  531. await request.WriteAsync(document).WithCancellation(token).ConfigureAwait(false);
  532. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  533. if (result == null)
  534. {
  535. throw new InvalidOperationException("Got null response from the server after doing a put on " + key + ", something is very wrong. Probably a garbled response.");
  536. }
  537. return convention.CreateSerializer().Deserialize<PutResult>(new RavenJTokenReader(result));
  538. }
  539. catch (ErrorResponseException e)
  540. {
  541. if (e.StatusCode != HttpStatusCode.Conflict) throw;
  542. responseException = e;
  543. }
  544. throw FetchConcurrencyException(responseException);
  545. }
  546. }
  547. public IAsyncDatabaseCommands ForDatabase(string database)
  548. {
  549. return ForDatabaseInternal(database);
  550. }
  551. public IAsyncDatabaseCommands ForSystemDatabase()
  552. {
  553. return ForSystemDatabaseInternal();
  554. }
  555. internal AsyncServerClient ForDatabaseInternal(string database)
  556. {
  557. if (database == Constants.SystemDatabase)
  558. return ForSystemDatabaseInternal();
  559. var databaseUrl = MultiDatabase.GetRootDatabaseUrl(Url).ForDatabase(database);
  560. if (databaseUrl == Url)
  561. return this;
  562. return new AsyncServerClient(databaseUrl, convention,
  563. credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication,
  564. jsonRequestFactory, sessionId,
  565. requestExecuterGetter, requestTimeMetricGetter, database, conflictListeners, false)
  566. {
  567. operationsHeaders = operationsHeaders
  568. };
  569. }
  570. internal AsyncServerClient ForSystemDatabaseInternal()
  571. {
  572. var databaseUrl = MultiDatabase.GetRootDatabaseUrl(Url);
  573. if (databaseUrl == Url)
  574. return this;
  575. return new AsyncServerClient(databaseUrl, convention, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, jsonRequestFactory, sessionId, requestExecuterGetter, requestTimeMetricGetter, null, conflictListeners, false) { operationsHeaders = operationsHeaders };
  576. }
  577. public NameValueCollection OperationsHeaders
  578. {
  579. get { return operationsHeaders; }
  580. set { operationsHeaders = value; }
  581. }
  582. public IAsyncGlobalAdminDatabaseCommands GlobalAdmin { get { return new AsyncAdminServerClient(this); } }
  583. public IAsyncAdminDatabaseCommands Admin { get { return new AsyncAdminServerClient(this); } }
  584. public Task<JsonDocument> GetAsync(string key, CancellationToken token = default(CancellationToken))
  585. {
  586. EnsureIsNotNullOrEmpty(key, "key");
  587. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectGetAsync(operationMetadata, requestTimeMetric, key, token), token);
  588. }
  589. public Task<TransformerDefinition> GetTransformerAsync(string name, CancellationToken token = default(CancellationToken))
  590. {
  591. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  592. {
  593. try
  594. {
  595. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Transformer(name), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  596. {
  597. var transformerDefinitionJson = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  598. var value = transformerDefinitionJson.Value<RavenJObject>("Transformer");
  599. return convention.CreateSerializer().Deserialize<TransformerDefinition>(new RavenJTokenReader(value));
  600. }
  601. }
  602. catch (ErrorResponseException we)
  603. {
  604. if (we.StatusCode == HttpStatusCode.NotFound)
  605. return null;
  606. throw;
  607. }
  608. }, token);
  609. }
  610. public Task<IndexDefinition> GetIndexAsync(string name, CancellationToken token = default(CancellationToken))
  611. {
  612. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  613. {
  614. try
  615. {
  616. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.IndexDefinition(name), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  617. {
  618. var indexDefinitionJson = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  619. var value = indexDefinitionJson.Value<RavenJObject>("Index");
  620. return convention.CreateSerializer().Deserialize<IndexDefinition>(new RavenJTokenReader(value));
  621. }
  622. }
  623. catch (ErrorResponseException we)
  624. {
  625. if (we.StatusCode == HttpStatusCode.NotFound)
  626. return null;
  627. throw;
  628. }
  629. }, token);
  630. }
  631. public Task<IndexingPerformanceStatistics[]> GetIndexingPerformanceStatisticsAsync()
  632. {
  633. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  634. {
  635. var url = operationMetadata.Url.IndexingPerformanceStatistics();
  636. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, url, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  637. {
  638. var indexingPerformanceStatisticsJson = (RavenJArray)await request.ReadResponseJsonAsync().ConfigureAwait(false);
  639. var results = new IndexingPerformanceStatistics[indexingPerformanceStatisticsJson.Length];
  640. for (var i = 0; i < indexingPerformanceStatisticsJson.Length; i++)
  641. {
  642. var stats = (RavenJObject)indexingPerformanceStatisticsJson[i];
  643. results[i] = stats.Deserialize<IndexingPerformanceStatistics>(convention);
  644. }
  645. return results;
  646. }
  647. });
  648. }
  649. private async Task<JsonDocument> DirectGetAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, CancellationToken token)
  650. {
  651. if (key.Length > 127)
  652. {
  653. // avoid hitting UrlSegmentMaxLength limits in Http.sys
  654. var multiLoadResult = await DirectGetAsync(operationMetadata, requestTimeMetric, new[] { key }, new string[0], null, new Dictionary<string, RavenJToken>(), false, token).WithCancellation(token).ConfigureAwait(false);
  655. var result = multiLoadResult.Results.FirstOrDefault();
  656. if (result == null)
  657. return null;
  658. return SerializationHelper.RavenJObjectToJsonDocument(result);
  659. }
  660. var metadata = new RavenJObject();
  661. AddTransactionInformation(metadata);
  662. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this,
  663. (operationMetadata.Url + "/docs?id=" + Uri.EscapeDataString(key)),
  664. HttpMethod.Get,
  665. metadata,
  666. operationMetadata.Credentials,
  667. convention,
  668. requestTimeMetric);
  669. using (var request = jsonRequestFactory.CreateHttpJsonRequest(
  670. createHttpJsonRequestParams.AddOperationHeaders(OperationsHeaders))
  671. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  672. {
  673. Task<JsonDocument> resolveConflictTask;
  674. try
  675. {
  676. var requestJson = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  677. var docKey = request.ResponseHeaders.Get(Constants.DocumentIdFieldName) ?? key;
  678. docKey = Uri.UnescapeDataString(docKey);
  679. request.ResponseHeaders.Remove(Constants.DocumentIdFieldName);
  680. var deserializeJsonDocument = SerializationHelper.DeserializeJsonDocument(docKey, requestJson, request.ResponseHeaders, request.ResponseStatusCode);
  681. return deserializeJsonDocument;
  682. }
  683. catch (ErrorResponseException e)
  684. {
  685. switch (e.StatusCode)
  686. {
  687. case HttpStatusCode.NotFound:
  688. return null;
  689. case HttpStatusCode.Conflict:
  690. resolveConflictTask = ResolveConflict(e.ResponseString, e.Etag, operationMetadata, requestTimeMetric, key, token);
  691. break;
  692. default:
  693. throw;
  694. }
  695. }
  696. return await resolveConflictTask.WithCancellation(token).ConfigureAwait(false);
  697. }
  698. }
  699. private async Task<JsonDocument> ResolveConflict(string httpResponse, Etag etag, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, CancellationToken token)
  700. {
  701. var conflicts = new StringReader(httpResponse);
  702. var conflictsDoc = RavenJObject.Load(new RavenJsonTextReader(conflicts));
  703. var result =
  704. await TryResolveConflictOrCreateConcurrencyException(operationMetadata, requestTimeMetric, key, conflictsDoc, etag, token).ConfigureAwait(false);
  705. if (result != null)
  706. throw result;
  707. return await DirectGetAsync(operationMetadata, requestTimeMetric, key, token).ConfigureAwait(false);
  708. }
  709. public Task<MultiLoadResult> GetAsync(string[] keys, string[] includes, string transformer = null,
  710. Dictionary<string, RavenJToken> transformerParameters = null, bool metadataOnly = false, CancellationToken token = default(CancellationToken))
  711. {
  712. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectGetAsync(operationMetadata, requestTimeMetric, keys, includes, transformer, transformerParameters, metadataOnly, token), token);
  713. }
  714. private async Task<MultiLoadResult> DirectGetAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string[] keys, string[] includes, string transformer,
  715. Dictionary<string, RavenJToken> transformerParameters, bool metadataOnly, CancellationToken token = default(CancellationToken))
  716. {
  717. var path = operationMetadata.Url + "/queries/?";
  718. if (metadataOnly)
  719. path += "&metadata-only=true";
  720. if (includes != null && includes.Length > 0)
  721. {
  722. path += string.Join("&", includes.Select(x => "include=" + x).ToArray());
  723. }
  724. if (string.IsNullOrEmpty(transformer) == false)
  725. path += "&transformer=" + transformer;
  726. if (transformerParameters != null)
  727. {
  728. path = transformerParameters.Aggregate(path,
  729. (current, transformerParam) =>
  730. current + ("&" + string.Format("tp-{0}={1}", transformerParam.Key, transformerParam.Value)));
  731. }
  732. var metadata = new RavenJObject();
  733. AddTransactionInformation(metadata);
  734. var uniqueIds = new HashSet<string>(keys);
  735. // if it is too big, we drop to POST (note that means that we can't use the HTTP cache any longer)
  736. // we are fine with that, requests to load > 128 items are going to be rare
  737. var isGet = uniqueIds.Sum(x => x.Length) < 1024;
  738. var method = isGet ? HttpMethod.Get : HttpMethod.Post;
  739. if (isGet)
  740. {
  741. path += "&" + string.Join("&", uniqueIds.Select(x => "id=" + Uri.EscapeDataString(x)).ToArray());
  742. }
  743. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, path, method, metadata, operationMetadata.Credentials, convention)
  744. .AddOperationHeaders(OperationsHeaders);
  745. using (var request = jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams)
  746. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  747. {
  748. if (isGet == false)
  749. {
  750. await request.WriteAsync(new RavenJArray(uniqueIds)).WithCancellation(token).ConfigureAwait(false);
  751. }
  752. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  753. return await CompleteMultiGetAsync(operationMetadata, requestTimeMetric, keys, includes, transformer, transformerParameters, result, token).ConfigureAwait(false);
  754. }
  755. }
  756. private async Task<MultiLoadResult> CompleteMultiGetAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string[] keys, string[] includes, string transformer,
  757. Dictionary<string, RavenJToken> transformerParameters, RavenJToken result, CancellationToken token = default(CancellationToken))
  758. {
  759. ErrorResponseException responseException;
  760. try
  761. {
  762. var uniqueKeys = new HashSet<string>(keys);
  763. var results = result
  764. .Value<RavenJArray>("Results")
  765. .Select(x => x as RavenJObject)
  766. .ToList();
  767. var documents = results
  768. .Where(x => x != null && x.ContainsKey("@metadata") && x["@metadata"].Value<string>("@id") != null)
  769. .ToDictionary(x => x["@metadata"].Value<string>("@id"), x => x, StringComparer.OrdinalIgnoreCase);
  770. if (results.Count >= uniqueKeys.Count)
  771. {
  772. for (var i = 0; i < uniqueKeys.Count; i++)
  773. {
  774. va

Large files files are truncated, but you can click here to view the full file