PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/ToMigrate/Raven.Database/Server/Controllers/DebugController.cs

http://github.com/ayende/ravendb
C# | 1003 lines | 951 code | 33 blank | 19 comment | 17 complexity | 4da961052a2e5cd531f22e83bb57e3bf 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
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.IO.Compression;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Net.Http;
  11. using System.Net.Http.Headers;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using System.Web.Http;
  15. using System.Web.Http.Controllers;
  16. using System.Web.Http.Routing;
  17. using ICSharpCode.NRefactory.CSharp;
  18. using Raven.Abstractions;
  19. using Raven.Abstractions.Counters;
  20. using Raven.Abstractions.Data;
  21. using Raven.Abstractions.Logging;
  22. using Raven.Abstractions.Util;
  23. using Raven.Database.Bundles.SqlReplication;
  24. using Raven.Database.Linq;
  25. using Raven.Database.Linq.Ast;
  26. using Raven.Database.Server.WebApi;
  27. using Raven.Database.Server.WebApi.Attributes;
  28. using Raven.Database.Storage;
  29. using Raven.Database.Util;
  30. using Raven.Json.Linq;
  31. using IOExtensions = Raven.Database.Extensions.IOExtensions;
  32. namespace Raven.Database.Server.Controllers
  33. {
  34. [RoutePrefix("")]
  35. public class DebugController : BaseDatabaseApiController
  36. {
  37. public class CounterDebugInfo
  38. {
  39. public int ReplicationActiveTasksCount { get; set; }
  40. public IDictionary<string,CounterDestinationStats> ReplicationDestinationStats { get; set; }
  41. public CounterStorageStats Summary { get; set; }
  42. public DateTime LastWrite { get; set; }
  43. public Guid ServerId { get; set; }
  44. public AtomicDictionary<object> ExtensionsState { get; set; }
  45. }
  46. [HttpGet]
  47. [RavenRoute("cs/debug/counter-storages")]
  48. public HttpResponseMessage GetCounterStoragesInfo()
  49. {
  50. var infos = new List<CounterDebugInfo>();
  51. CountersLandlord.ForAllCounters(counterStorage =>
  52. infos.Add(new CounterDebugInfo
  53. {
  54. ReplicationActiveTasksCount = counterStorage.ReplicationTask.GetActiveTasksCount(),
  55. ReplicationDestinationStats = counterStorage.ReplicationTask.DestinationStats,
  56. LastWrite = counterStorage.LastWrite,
  57. ServerId = counterStorage.ServerId,
  58. Summary = counterStorage.CreateStats(),
  59. ExtensionsState = counterStorage.ExtensionsState
  60. }));
  61. return GetMessageWithObject(infos);
  62. }
  63. [HttpGet]
  64. [RavenRoute("cs/{counterStorageName}/debug/")]
  65. public async Task<HttpResponseMessage> GetCounterNames(string counterStorageName,int skip, int take)
  66. {
  67. var counter = await CountersLandlord.GetResourceInternal(counterStorageName).ConfigureAwait(false);
  68. if (counter == null)
  69. return GetMessageWithString(string.Format("Counter storage with name {0} not found.", counterStorageName),HttpStatusCode.NotFound);
  70. using (var reader = counter.CreateReader())
  71. {
  72. var groupsAndNames = reader.GetCounterGroups(0, int.MaxValue)
  73. .SelectMany(group => reader.GetCounterSummariesByGroup(group.Name, 0, int.MaxValue)
  74. .Select(x => new CounterNameGroupPair
  75. {
  76. Name = x.CounterName,
  77. Group = group.Name
  78. }));
  79. return GetMessageWithObject(new
  80. {
  81. Stats = counter.CreateStats(),
  82. HasMore = groupsAndNames.Count() > skip + take,
  83. GroupsAndNames = groupsAndNames.Skip(skip).Take(take)
  84. });
  85. }
  86. }
  87. [HttpGet]
  88. [RavenRoute("cs/{counterStorageName}/debug/metrics")]
  89. public async Task<HttpResponseMessage> GetCounterMetrics(string counterStorageName)
  90. {
  91. var counter = await CountersLandlord.GetResourceInternal(counterStorageName).ConfigureAwait(false);
  92. if (counter == null)
  93. return GetMessageWithString(string.Format("Counter storage with name {0} not found.", counterStorageName), HttpStatusCode.NotFound);
  94. return GetMessageWithObject(counter.CreateMetrics());
  95. }
  96. [HttpGet]
  97. [RavenRoute("debug/cache-details")]
  98. [RavenRoute("databases/{databaseName}/debug/cache-details")]
  99. public HttpResponseMessage CacheDetails()
  100. {
  101. return GetMessageWithObject(Database.TransactionalStorage.DocumentCacher.GetStatistics());
  102. }
  103. [HttpGet]
  104. [RavenRoute("debug/enable-query-timing")]
  105. [RavenRoute("databases/{databaseName}/debug/enable-query-timing")]
  106. public HttpResponseMessage EnableQueryTiming()
  107. {
  108. var time = SystemTime.UtcNow + TimeSpan.FromMinutes(5);
  109. if (Database.IsSystemDatabase())
  110. {
  111. DatabasesLandlord.ForAllDatabases(database => database.WorkContext.ShowTimingByDefaultUntil = time);
  112. }
  113. else
  114. {
  115. Database.WorkContext.ShowTimingByDefaultUntil = time;
  116. }
  117. return GetMessageWithObject(new { Enabled = true, Until = time });
  118. }
  119. [HttpGet]
  120. [RavenRoute("debug/disable-query-timing")]
  121. [RavenRoute("databases/{databaseName}/debug/disable-query-timing")]
  122. public HttpResponseMessage DisableQueryTiming()
  123. {
  124. if (Database.IsSystemDatabase())
  125. {
  126. DatabasesLandlord.ForAllDatabases(database => database.WorkContext.ShowTimingByDefaultUntil = null);
  127. }
  128. else
  129. {
  130. Database.WorkContext.ShowTimingByDefaultUntil = null;
  131. }
  132. return GetMessageWithObject(new { Enabled = false });
  133. }
  134. [HttpGet]
  135. [RavenRoute("debug/prefetch-status")]
  136. [RavenRoute("databases/{databaseName}/debug/prefetch-status")]
  137. public HttpResponseMessage PrefetchingQueueStatus()
  138. {
  139. return GetMessageWithObject(DebugInfoProvider.GetPrefetchingQueueStatusForDebug(Database));
  140. }
  141. [HttpPost]
  142. [RavenRoute("debug/format-index")]
  143. [RavenRoute("databases/{databaseName}/debug/format-index")]
  144. public async Task<HttpResponseMessage> FormatIndex()
  145. {
  146. RavenJArray array;
  147. try
  148. {
  149. array = await ReadJsonArrayAsync().ConfigureAwait(false);
  150. }
  151. catch (InvalidOperationException e)
  152. {
  153. if (Log.IsDebugEnabled)
  154. Log.DebugException("Failed to deserialize debug request.", e);
  155. return GetMessageWithObject(new
  156. {
  157. Message = "Could not understand json, please check its validity."
  158. }, (HttpStatusCode)422); //http code 422 - Unprocessable entity
  159. }
  160. catch (InvalidDataException e)
  161. {
  162. if (Log.IsDebugEnabled)
  163. Log.DebugException("Failed to deserialize debug request." , e);
  164. return GetMessageWithObject(new
  165. {
  166. e.Message
  167. }, (HttpStatusCode)422); //http code 422 - Unprocessable entity
  168. }
  169. var results = new string[array.Length];
  170. for (int i = 0; i < array.Length; i++)
  171. {
  172. var value = array[i].Value<string>();
  173. try
  174. {
  175. results[i] = IndexPrettyPrinter.FormatOrError(value);
  176. }
  177. catch (Exception e)
  178. {
  179. results[i] = "Could not format:" + Environment.NewLine +
  180. value + Environment.NewLine + e;
  181. }
  182. }
  183. return GetMessageWithObject(results);
  184. }
  185. /// <remarks>
  186. /// as we sum data we have to guarantee that we don't sum the same record twice on client side.
  187. /// to prevent such situation we don't send data from current second
  188. /// </remarks>
  189. /// <returns></returns>
  190. [HttpGet]
  191. [RavenRoute("debug/sql-replication-perf-stats")]
  192. [RavenRoute("databases/{databaseName}/debug/sql-replication-perf-stats")]
  193. public HttpResponseMessage SqlReplicationPerfStats()
  194. {
  195. var now = SystemTime.UtcNow;
  196. var nowTruncToSeconds = new DateTime(now.Ticks / TimeSpan.TicksPerSecond * TimeSpan.TicksPerSecond, now.Kind);
  197. var sqlReplicationTask = Database.StartupTasks.OfType<SqlReplicationTask>().FirstOrDefault();
  198. if (sqlReplicationTask == null)
  199. {
  200. return GetMessageWithString("Unable to find SQL Replication task. Maybe it is not enabled?", HttpStatusCode.BadRequest);
  201. }
  202. var stats = from nameAndStatsManager in sqlReplicationTask.SqlReplicationMetricsCounters
  203. from perf in nameAndStatsManager.Value.ReplicationPerformanceStats
  204. where perf.Started < nowTruncToSeconds
  205. let k = new { Name = nameAndStatsManager.Key, perf }
  206. group k by k.perf.Started.Ticks / TimeSpan.TicksPerSecond
  207. into g
  208. orderby g.Key
  209. select new
  210. {
  211. Started = new DateTime(g.Key * TimeSpan.TicksPerSecond, DateTimeKind.Utc),
  212. Stats = from k in g
  213. group k by k.Name into gg
  214. select new
  215. {
  216. ReplicationName = gg.Key,
  217. DurationMilliseconds = gg.Sum(x => x.perf.DurationMilliseconds),
  218. BatchSize = gg.Sum(x => x.perf.BatchSize)
  219. }
  220. };
  221. return GetMessageWithObject(stats);
  222. }
  223. /// <remarks>
  224. /// as we sum data we have to guarantee that we don't sum the same record twice on client side.
  225. /// to prevent such situation we don't send data from current second
  226. /// </remarks>
  227. /// <returns></returns>
  228. [HttpGet]
  229. [RavenRoute("debug/replication-perf-stats")]
  230. [RavenRoute("databases/{databaseName}/debug/replication-perf-stats")]
  231. public HttpResponseMessage ReplicationPerfStats()
  232. {
  233. var now = SystemTime.UtcNow;
  234. var nowTruncToSeconds = new DateTime(now.Ticks / TimeSpan.TicksPerSecond * TimeSpan.TicksPerSecond, now.Kind);
  235. var stats = from nameAndStatsManager in Database.WorkContext.MetricsCounters.ReplicationPerformanceStats
  236. from perf in nameAndStatsManager.Value
  237. where perf.Started < nowTruncToSeconds
  238. let k = new { Name = nameAndStatsManager.Key, perf }
  239. group k by k.perf.Started.Ticks / TimeSpan.TicksPerSecond
  240. into g
  241. orderby g.Key
  242. select new
  243. {
  244. Started = new DateTime(g.Key * TimeSpan.TicksPerSecond, DateTimeKind.Utc),
  245. Stats = from k in g
  246. group k by k.Name into gg
  247. select new
  248. {
  249. Destination = gg.Key,
  250. DurationMilliseconds = gg.Sum(x => x.perf.DurationMilliseconds),
  251. BatchSize = gg.Sum(x => x.perf.BatchSize)
  252. }
  253. };
  254. return GetMessageWithObject(stats);
  255. }
  256. /// <summary>
  257. ///
  258. /// </summary>
  259. /// <remarks>
  260. /// as we sum data we have to guarantee that we don't sum the same record twice on client side.
  261. /// to prevent such situation we don't send data from current second
  262. /// </remarks>
  263. /// <param name="format"></param>
  264. /// <returns></returns>
  265. [HttpGet]
  266. [RavenRoute("debug/indexing-perf-stats-with-timings")]
  267. [RavenRoute("databases/{databaseName}/debug/indexing-perf-stats-with-timings")]
  268. public HttpResponseMessage IndexingPerfStatsWthTimings(string format = "json")
  269. {
  270. var now = SystemTime.UtcNow;
  271. var nowTruncToSeconds = new DateTime(now.Ticks / TimeSpan.TicksPerSecond * TimeSpan.TicksPerSecond, now.Kind);
  272. var stats = from pair in Database.IndexDefinitionStorage.IndexDefinitions
  273. let performance = Database.IndexStorage.GetIndexingPerformance(pair.Key)
  274. from perf in performance
  275. where (perf.Operation == "Map" || perf.Operation == "Index") && perf.Started < nowTruncToSeconds
  276. let k = new { IndexDefinition = pair.Value, Performance = perf }
  277. group k by k.Performance.Started.Ticks / TimeSpan.TicksPerSecond into g
  278. orderby g.Key
  279. select new
  280. {
  281. Started = new DateTime(g.Key * TimeSpan.TicksPerSecond, DateTimeKind.Utc),
  282. Stats = from k in g
  283. group k by k.IndexDefinition.Name into gg
  284. select new
  285. {
  286. Index = gg.Key,
  287. DurationMilliseconds = gg.Sum(x => x.Performance.DurationMilliseconds),
  288. InputCount = gg.Sum(x => x.Performance.InputCount),
  289. OutputCount = gg.Sum(x => x.Performance.OutputCount),
  290. ItemsCount = gg.Sum(x => x.Performance.ItemsCount)
  291. }
  292. };
  293. switch (format)
  294. {
  295. case "csv":
  296. case "CSV":
  297. var sw = new StringWriter();
  298. sw.WriteLine();
  299. foreach (var stat in stats)
  300. {
  301. sw.WriteLine(stat.Started.ToString("o"));
  302. sw.WriteLine("Index, Duration (ms), Input, Output, Items");
  303. foreach (var indexStat in stat.Stats)
  304. {
  305. sw.Write('"');
  306. sw.Write(indexStat.Index);
  307. sw.Write("\",{0},{1},{2},{3}", indexStat.DurationMilliseconds, indexStat.InputCount, indexStat.OutputCount, indexStat.ItemsCount);
  308. sw.WriteLine();
  309. }
  310. sw.WriteLine();
  311. }
  312. var msg = sw.GetStringBuilder().ToString();
  313. return new HttpResponseMessage(HttpStatusCode.OK)
  314. {
  315. Content = new MultiGetSafeStringContent(msg)
  316. {
  317. Headers =
  318. {
  319. ContentType = new MediaTypeHeaderValue("text/plain")
  320. }
  321. }
  322. };
  323. default:
  324. return GetMessageWithObject(stats);
  325. }
  326. }
  327. [HttpGet]
  328. [RavenRoute("debug/filtered-out-indexes")]
  329. [RavenRoute("databases/{databaseName}/debug/filtered-out-indexes")]
  330. public HttpResponseMessage FilteredOutIndexes()
  331. {
  332. return GetMessageWithObject(Database.WorkContext.RecentlyFilteredOutIndexes.ToArray());
  333. }
  334. [HttpGet]
  335. [RavenRoute("debug/indexing-batch-stats")]
  336. [RavenRoute("databases/{databaseName}/debug/indexing-batch-stats")]
  337. public HttpResponseMessage IndexingBatchStats(int lastId = 0)
  338. {
  339. var indexingBatches = Database.WorkContext.LastActualIndexingBatchInfo.ToArray();
  340. var indexingBatchesTrimmed = indexingBatches.SkipWhile(x => x.Id < lastId).ToArray();
  341. return GetMessageWithObject(indexingBatchesTrimmed);
  342. }
  343. [HttpGet]
  344. [RavenRoute("debug/reducing-batch-stats")]
  345. [RavenRoute("databases/{databaseName}/debug/reducing-batch-stats")]
  346. public HttpResponseMessage ReducingBatchStats(int lastId = 0)
  347. {
  348. var reducingBatches = Database.WorkContext.LastActualReducingBatchInfo.ToArray();
  349. var reducingBatchesTrimmed = reducingBatches.SkipWhile(x => x.Id < lastId).ToArray();
  350. return GetMessageWithObject(reducingBatchesTrimmed);
  351. }
  352. [HttpGet]
  353. [RavenRoute("debug/plugins")]
  354. [RavenRoute("databases/{databaseName}/debug/plugins")]
  355. public HttpResponseMessage Plugins()
  356. {
  357. return GetMessageWithObject(Database.PluginsInfo);
  358. }
  359. [HttpGet]
  360. [RavenRoute("debug/changes")]
  361. [RavenRoute("databases/{databaseName}/debug/changes")]
  362. public HttpResponseMessage Changes()
  363. {
  364. return GetMessageWithObject(Database.TransportState.DebugStatuses);
  365. }
  366. [HttpGet]
  367. [RavenRoute("debug/sql-replication-stats")]
  368. [RavenRoute("databases/{databaseName}/debug/sql-replication-stats")]
  369. public HttpResponseMessage SqlReplicationStats()
  370. {
  371. var task = Database.StartupTasks.OfType<SqlReplicationTask>().FirstOrDefault();
  372. if (task == null)
  373. return GetMessageWithObject(new
  374. {
  375. Error = "SQL Replication bundle is not installed"
  376. }, HttpStatusCode.NotFound);
  377. //var metrics = task.SqlReplicationMetricsCounters.ToDictionary(x => x.Key, x => x.Value.ToSqlReplicationMetricsData());
  378. var statisticsAndMetrics = task.GetConfiguredReplicationDestinations().Select(x =>
  379. {
  380. SqlReplicationStatistics stats;
  381. task.Statistics.TryGetValue(x.Name, out stats);
  382. var metrics = task.GetSqlReplicationMetricsManager(x).ToSqlReplicationMetricsData();
  383. return new
  384. {
  385. x.Name,
  386. Statistics = stats,
  387. Metrics = metrics
  388. };
  389. });
  390. return GetMessageWithObject(statisticsAndMetrics);
  391. }
  392. [HttpGet]
  393. [RavenRoute("debug/metrics")]
  394. [RavenRoute("databases/{databaseName}/debug/metrics")]
  395. public HttpResponseMessage Metrics()
  396. {
  397. return GetMessageWithObject(Database.CreateMetrics());
  398. }
  399. [HttpGet]
  400. [RavenRoute("debug/config")]
  401. [RavenRoute("databases/{databaseName}/debug/config")]
  402. public HttpResponseMessage Config()
  403. {
  404. if (CanExposeConfigOverTheWire() == false)
  405. {
  406. return GetEmptyMessage(HttpStatusCode.Forbidden);
  407. }
  408. return GetMessageWithObject(DebugInfoProvider.GetConfigForDebug(Database));
  409. }
  410. [HttpGet]
  411. [RavenRoute("debug/raw-doc")]
  412. [RavenRoute("databases/{databaseName}/debug/raw-doc")]
  413. public HttpResponseMessage RawDocBytes()
  414. {
  415. var docId = GetQueryStringValue("id");
  416. if (String.IsNullOrWhiteSpace(docId))
  417. return GetMessageWithObject(new
  418. {
  419. Error = "Query string 'id' is mandatory"
  420. }, HttpStatusCode.BadRequest);
  421. bool hasDoc = false;
  422. Database.TransactionalStorage.Batch(accessor =>
  423. {
  424. using (var s = accessor.Documents.RawDocumentByKey(docId))
  425. {
  426. hasDoc = s != null;
  427. }
  428. });
  429. if (hasDoc == false)
  430. {
  431. return GetMessageWithObject(new
  432. {
  433. Error = "No document with id " + docId + " was found"
  434. }, HttpStatusCode.NotFound);
  435. }
  436. return new HttpResponseMessage(HttpStatusCode.OK)
  437. {
  438. Content = new PushStreamContent((stream, content, transportContext) => Database.TransactionalStorage.Batch(accessor =>
  439. {
  440. using (stream)
  441. using (var docContent = accessor.Documents.RawDocumentByKey(docId))
  442. {
  443. docContent.CopyTo(stream);
  444. stream.Flush();
  445. }
  446. }))
  447. {
  448. Headers =
  449. {
  450. ContentType = new MediaTypeHeaderValue("application/octet-stream"),
  451. ContentDisposition = new ContentDispositionHeaderValue("attachment")
  452. {
  453. FileName = docId + ".raw-doc"
  454. }
  455. }
  456. }
  457. };
  458. }
  459. [HttpGet]
  460. [RavenRoute("debug/slow-dump-ref-csv")]
  461. [RavenRoute("databases/{databaseName}/debug/slow-dump-ref-csv")]
  462. public HttpResponseMessage DumpRefsToCsv(int sampleCount = 10)
  463. {
  464. return new HttpResponseMessage
  465. {
  466. Content = new PushStreamContent((stream, content, context) =>
  467. {
  468. using (var writer = new StreamWriter(stream))
  469. {
  470. writer.WriteLine("ref count,document key,sample references");
  471. Database.TransactionalStorage.Batch(accessor =>
  472. {
  473. accessor.Indexing.DumpAllReferancesToCSV(writer, sampleCount);
  474. });
  475. writer.Flush();
  476. stream.Flush();
  477. }
  478. })
  479. {
  480. Headers =
  481. {
  482. ContentDisposition = new ContentDispositionHeaderValue("attachment")
  483. {
  484. FileName = "doc-refs.csv",
  485. },
  486. ContentType = new MediaTypeHeaderValue("application/octet-stream")
  487. }
  488. }
  489. };
  490. }
  491. [HttpGet]
  492. [RavenRoute("debug/docrefs")]
  493. [RavenRoute("databases/{databaseName}/debug/docrefs")]
  494. public HttpResponseMessage DocRefs(string id)
  495. {
  496. var op = GetQueryStringValue("op");
  497. op = op == "from" ? "from" : "to";
  498. var totalCountReferencing = -1;
  499. List<string> results = null;
  500. Database.TransactionalStorage.Batch(accessor =>
  501. {
  502. totalCountReferencing = accessor.Indexing.GetCountOfDocumentsReferencing(id);
  503. var documentsReferencing =
  504. op == "from"
  505. ? accessor.Indexing.GetDocumentsReferencesFrom(id)
  506. : accessor.Indexing.GetDocumentsReferencing(id);
  507. results = documentsReferencing.Skip(GetStart()).Take(GetPageSize(Database.Configuration.Core.MaxPageSize)).ToList();
  508. });
  509. return GetMessageWithObject(new
  510. {
  511. TotalCountReferencing = totalCountReferencing,
  512. Results = results
  513. });
  514. }
  515. //DumpAllReferancesToCSV
  516. [HttpGet]
  517. [RavenRoute("debug/d0crefs-t0ps")]
  518. [RavenRoute("databases/{databaseName}/debug/d0crefs-t0ps")]
  519. public HttpResponseMessage DocRefsTops()
  520. {
  521. var sp = Stopwatch.StartNew();
  522. Dictionary<string, int> documentReferencesStats = null;
  523. Database.TransactionalStorage.Batch(accessor =>
  524. {
  525. documentReferencesStats = accessor.Indexing.GetDocumentReferencesStats();
  526. });
  527. return GetMessageWithObject(new
  528. {
  529. TotalReferences = documentReferencesStats.Count,
  530. GenerationCost = sp.Elapsed,
  531. Results = documentReferencesStats.OrderByDescending(x => x.Value)
  532. .Select(x => new { Document = x.Key, Count = x.Value })
  533. });
  534. }
  535. [HttpPost]
  536. [RavenRoute("debug/index-fields")]
  537. [RavenRoute("databases/{databaseName}/debug/index-fields")]
  538. public async Task<HttpResponseMessage> IndexFields()
  539. {
  540. var indexStr = await ReadStringAsync().ConfigureAwait(false);
  541. bool querySyntax = indexStr.Trim().StartsWith("from");
  542. var mapDefinition = querySyntax
  543. ? QueryParsingUtils.GetVariableDeclarationForLinqQuery(indexStr, true)
  544. : QueryParsingUtils.GetVariableDeclarationForLinqMethods(indexStr, true);
  545. var captureSelectNewFieldNamesVisitor = new CaptureSelectNewFieldNamesVisitor(querySyntax == false, new HashSet<string>(), new Dictionary<string, Expression>());
  546. mapDefinition.AcceptVisitor(captureSelectNewFieldNamesVisitor, null);
  547. return GetMessageWithObject(new { FieldNames = captureSelectNewFieldNamesVisitor.FieldNames });
  548. }
  549. [HttpGet]
  550. [RavenRoute("debug/list")]
  551. [RavenRoute("databases/{databaseName}/debug/list")]
  552. public HttpResponseMessage List(string id)
  553. {
  554. var listName = id;
  555. var key = InnerRequest.RequestUri.ParseQueryString()["key"];
  556. if (key == null)
  557. throw new ArgumentException("Key query string variable is mandatory");
  558. ListItem listItem = null;
  559. Database.TransactionalStorage.Batch(accessor =>
  560. {
  561. listItem = accessor.Lists.Read(listName, key);
  562. });
  563. if (listItem == null)
  564. return GetEmptyMessage(HttpStatusCode.NotFound);
  565. return GetMessageWithObject(listItem);
  566. }
  567. [HttpGet]
  568. [RavenRoute("debug/list-all")]
  569. [RavenRoute("databases/{databaseName}/debug/list-all")]
  570. public HttpResponseMessage ListAll(string id)
  571. {
  572. var listName = id;
  573. List<ListItem> listItems = null;
  574. Database.TransactionalStorage.Batch(accessor =>
  575. {
  576. listItems = accessor.Lists.Read(listName, Etag.Empty, null, GetPageSize(Database.Configuration.Core.MaxPageSize)).ToList();
  577. });
  578. if (listItems == null)
  579. return GetEmptyMessage(HttpStatusCode.NotFound);
  580. return GetMessageWithObject(listItems);
  581. }
  582. [HttpGet]
  583. [RavenRoute("debug/queries")]
  584. [RavenRoute("databases/{databaseName}/debug/queries")]
  585. public HttpResponseMessage Queries()
  586. {
  587. return GetMessageWithObject(Database.WorkContext.CurrentlyRunningQueries);
  588. }
  589. [HttpGet]
  590. [RavenRoute("debug/suggest-index-merge")]
  591. [RavenRoute("databases/{databaseName}/debug/suggest-index-merge")]
  592. public HttpResponseMessage IndexMerge()
  593. {
  594. var mergeIndexSuggestions = Database.WorkContext.IndexDefinitionStorage.ProposeIndexMergeSuggestions();
  595. return GetMessageWithObject(mergeIndexSuggestions);
  596. }
  597. [HttpGet]
  598. [RavenRoute("debug/sl0w-d0c-c0unts")]
  599. [RavenRoute("databases/{databaseName}/debug/sl0w-d0c-c0unts")]
  600. public HttpResponseMessage SlowDocCounts()
  601. {
  602. DebugDocumentStats stat = null;
  603. Database.TransactionalStorage.Batch(accessor =>
  604. {
  605. stat = accessor.Documents.GetDocumentStatsVerySlowly();
  606. });
  607. return GetMessageWithObject(stat);
  608. }
  609. [HttpGet]
  610. [RavenRoute("debug/user-info")]
  611. [RavenRoute("databases/{databaseName}/debug/user-info")]
  612. public HttpResponseMessage UserInfo()
  613. {
  614. var userInfo = GetUserInfo();
  615. return GetMessageWithObject(userInfo);
  616. }
  617. [HttpGet]
  618. [RavenRoute("debug/user-info")]
  619. [RavenRoute("databases/{databaseName}/debug/user-info")]
  620. public HttpResponseMessage GetUserPermission(string database, string method)
  621. {
  622. if (string.IsNullOrEmpty(database))
  623. {
  624. return GetMessageWithObject(new
  625. {
  626. Error = "The database paramater is mandatory"
  627. }, HttpStatusCode.BadGateway);
  628. }
  629. var info = GetUserInfo();
  630. var databases = info.Databases;
  631. var db = databases.Find(d => d.Database.Equals(database));
  632. if (db == null)
  633. {
  634. return GetMessageWithObject(new
  635. {
  636. Error = "The database "+ database+ " was not found on the server"
  637. }, HttpStatusCode.NotFound);
  638. }
  639. if (db.IsAdmin)
  640. {
  641. return GetMessageWithObject(new UserPermission
  642. {
  643. User = info.User,
  644. Database = db,
  645. Method = method,
  646. IsGranted = true,
  647. Reason = method + " allowed on " + database + " because user " + info.User + " has admin permissions"
  648. });
  649. }
  650. if (!db.IsReadOnly)
  651. {
  652. return GetMessageWithObject(new UserPermission
  653. {
  654. User = info.User,
  655. Database = db,
  656. Method = method,
  657. IsGranted = true,
  658. Reason = method + " allowed on " + database + " because user " + info.User + "has ReadWrite permissions"
  659. });
  660. }
  661. if (method != "HEAD" && method != "GET")
  662. {
  663. return GetMessageWithObject(new UserPermission
  664. {
  665. User = info.User,
  666. Database = db,
  667. Method = method,
  668. IsGranted = false,
  669. Reason = method + " rejected on" + database + "because user" + info.User + "has ReadOnly permissions"
  670. });
  671. }
  672. return GetMessageWithObject(new UserPermission
  673. {
  674. User = info.User,
  675. Database = db,
  676. Method = method,
  677. IsGranted = false,
  678. Reason = method + " allowed on" + database + "because user" + info.User + "has ReadOnly permissions"
  679. });
  680. }
  681. [HttpGet]
  682. [RavenRoute("debug/tasks")]
  683. [RavenRoute("databases/{databaseName}/debug/tasks")]
  684. public HttpResponseMessage Tasks()
  685. {
  686. return GetMessageWithObject(DebugInfoProvider.GetTasksForDebug(Database));
  687. }
  688. [HttpGet]
  689. [RavenRoute("debug/routes")]
  690. [Description(@"Output the debug information for all the supported routes in Raven Server.")]
  691. public HttpResponseMessage Routes()
  692. {
  693. var routes = new SortedDictionary<string, RouteInfo>();
  694. foreach (var route in ControllerContext.Configuration.Routes)
  695. {
  696. var inner = route as IEnumerable<IHttpRoute>;
  697. if (inner == null) continue;
  698. foreach (var httpRoute in inner)
  699. {
  700. var key = httpRoute.RouteTemplate;
  701. bool forDatabase = false;
  702. if (key.StartsWith("databases/{databaseName}/"))
  703. {
  704. key = key.Substring("databases/{databaseName}/".Length);
  705. forDatabase = true;
  706. }
  707. var data = new RouteInfo(key);
  708. if (routes.ContainsKey(key))
  709. data = routes[key];
  710. if (forDatabase)
  711. data.CanRunForSpecificDatabase = true;
  712. var actions = ((IEnumerable)httpRoute.DataTokens["actions"]).OfType<ReflectedHttpActionDescriptor>();
  713. foreach (var reflectedHttpActionDescriptor in actions)
  714. {
  715. foreach (var httpMethod in reflectedHttpActionDescriptor.SupportedHttpMethods)
  716. {
  717. if (data.Methods.Any(method => method.Name == httpMethod.Method))
  718. continue;
  719. string description = null;
  720. var descriptionAttribute =
  721. reflectedHttpActionDescriptor.MethodInfo.CustomAttributes.FirstOrDefault(attributeData => attributeData.AttributeType == typeof(DescriptionAttribute));
  722. if (descriptionAttribute != null)
  723. description = descriptionAttribute.ConstructorArguments[0].Value.ToString();
  724. data.Methods.Add(new Method
  725. {
  726. Name = httpMethod.Method,
  727. Description = description
  728. });
  729. }
  730. }
  731. routes[key] = data;
  732. }
  733. }
  734. return GetMessageWithObject(routes);
  735. }
  736. [HttpGet]
  737. [RavenRoute("debug/currently-indexing")]
  738. [RavenRoute("databases/{databaseName}/debug/currently-indexing")]
  739. public HttpResponseMessage CurrentlyIndexing()
  740. {
  741. return GetMessageWithObject(DebugInfoProvider.GetCurrentlyIndexingForDebug(Database));
  742. }
  743. [HttpGet]
  744. [RavenRoute("debug/remaining-reductions")]
  745. [RavenRoute("databases/{databaseName}/debug/remaining-reductions")]
  746. public HttpResponseMessage CurrentlyRemainingReductions()
  747. {
  748. return GetMessageWithObject(Database.GetRemainingScheduledReductions());
  749. }
  750. [HttpGet]
  751. [RavenRoute("debug/clear-remaining-reductions")]
  752. [RavenRoute("databases/{databaseName}/debug/clear-remaining-reductions")]
  753. public HttpResponseMessage ResetRemainingReductionsTracking()
  754. {
  755. Database.TransactionalStorage.ResetScheduledReductionsTracking();
  756. return GetEmptyMessage();
  757. }
  758. [HttpGet]
  759. [RavenRoute("debug/request-tracing")]
  760. [RavenRoute("databases/{databaseName}/debug/request-tracing")]
  761. public HttpResponseMessage RequestTracing()
  762. {
  763. if (CanExposeConfigOverTheWire() == false)
  764. {
  765. return GetEmptyMessage(HttpStatusCode.Forbidden);
  766. }
  767. return GetMessageWithObject(DebugInfoProvider.GetRequestTrackingForDebug(RequestManager, DatabaseName));
  768. }
  769. [HttpGet]
  770. [RavenRoute("debug/identities")]
  771. [RavenRoute("databases/{databaseName}/debug/identities")]
  772. public HttpResponseMessage Identities()
  773. {
  774. var start = GetStart();
  775. var pageSize = GetPageSize(1024);
  776. long totalCount = 0;
  777. IEnumerable<KeyValuePair<string, long>> identities = null;
  778. Database.TransactionalStorage.Batch(accessor => identities = accessor.General.GetIdentities(start, pageSize, out totalCount));
  779. return GetMessageWithObject(new
  780. {
  781. TotalCount = totalCount,
  782. Identities = identities
  783. });
  784. }
  785. [HttpGet]
  786. [RavenRoute("databases/{databaseName}/debug/info-package")]
  787. [RavenRoute("debug/info-package")]
  788. public HttpResponseMessage InfoPackage()
  789. {
  790. if (CanExposeConfigOverTheWire() == false)
  791. {
  792. return GetEmptyMessage(HttpStatusCode.Forbidden);
  793. }
  794. var tempFileName = Path.Combine(Database.Configuration.Core.TempPath, Path.GetRandomFileName());
  795. try
  796. {
  797. using (var file = new FileStream(tempFileName, FileMode.Create))
  798. using (var package = new ZipArchive(file, ZipArchiveMode.Create))
  799. {
  800. DebugInfoProvider.CreateInfoPackageForDatabase(package, Database, RequestManager, ClusterManager);
  801. }
  802. var response = new HttpResponseMessage();
  803. response.Content = new StreamContent(new FileStream(tempFileName, FileMode.Open, FileAccess.Read))
  804. {
  805. Headers =
  806. {
  807. ContentDisposition = new ContentDispositionHeaderValue("attachment")
  808. {
  809. FileName = string.Format("Debug-Info-{0}.zip", SystemTime.UtcNow),
  810. },
  811. ContentType = new MediaTypeHeaderValue("application/octet-stream")
  812. }
  813. };
  814. return response;
  815. }
  816. finally
  817. {
  818. IOExtensions.DeleteFile(tempFileName);
  819. }
  820. }
  821. [HttpGet]
  822. [RavenRoute("databases/{databaseName}/debug/transactions")]
  823. [RavenRoute("debug/transactions")]
  824. public HttpResponseMessage Transactions()
  825. {
  826. throw new NotSupportedException("DTC is not supported.");
  827. }
  828. [HttpGet]
  829. [RavenRoute("debug/subscriptions")]
  830. [RavenRoute("databases/{databaseName}/debug/subscriptions")]
  831. public HttpResponseMessage Subscriptions()
  832. {
  833. return GetMessageWithObject(Database.Subscriptions.GetDebugInfo());
  834. }
  835. [HttpGet]
  836. [RavenRoute("databases/{databaseName}/debug/thread-pool")]
  837. [RavenRoute("debug/thread-pool")]
  838. public HttpResponseMessage ThreadPool()
  839. {
  840. return GetMessageWithObject(new[]
  841. {
  842. new
  843. {
  844. Database.MappingThreadPool.Name,
  845. WaitingTasks = Database.MappingThreadPool.GetAllWaitingTasks().Select(x => x.Description),
  846. RunningTasks = Database.MappingThreadPool.GetRunningTasks().Select(x => x.Description),
  847. ThreadPoolStats = Database.MappingThreadPool.GetThreadPoolStats()
  848. },
  849. new
  850. {
  851. Database.ReducingThreadPool.Name,
  852. WaitingTasks = Database.ReducingThreadPool.GetAllWaitingTasks().Select(x => x.Description),
  853. RunningTasks = Database.ReducingThreadPool.GetRunningTasks().Select(x => x.Description),
  854. ThreadPoolStats = Database.ReducingThreadPool.GetThreadPoolStats()
  855. }
  856. });
  857. }
  858. [HttpGet]
  859. [RavenRoute("debug/indexing-perf-stats")]
  860. [RavenRoute("databases/{databaseName}/debug/indexing-perf-stats")]
  861. public HttpResponseMessage IndexingPerfStats()
  862. {
  863. return GetMessageWithObject(Database.IndexingPerformanceStatistics);
  864. }
  865. [HttpGet]
  866. [RavenRoute("debug/gc-info")]
  867. public HttpResponseMessage GCInfo()
  868. {
  869. return GetMessageWithObject(new GCInfo {LastForcedGCTime = RavenGC.LastForcedGCTime, MemoryBeforeLastForcedGC = RavenGC.MemoryBeforeLastForcedGC, MemoryAfterLastForcedGC = RavenGC.MemoryAfterLastForcedGC});
  870. }
  871. }
  872. public class RouteInfo
  873. {
  874. public string Key { get; set; }
  875. public List<Method> Methods { get; set; }
  876. public bool CanRunForSpecificDatabase { get; set; }
  877. public RouteInfo(string key)
  878. {
  879. Key = key;
  880. Methods = new List<Method>();
  881. }
  882. }
  883. public class Method
  884. {
  885. public string Name { get; set; }
  886. public string Description { get; set; }
  887. }
  888. }