PageRenderTime 6ms CodeModel.GetById 8ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 1ms

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