Bug do IntelliSense?! Analisando o consumo excessivo de DTUs pelo IntelliSense
Apresentamos aqui um caso curioso que parece ser um bug do Microsoft IntelliSense que faz ele consumir recursos consideráveis do ambiente. Antes de entrarmos nos detalhes, vamos dar uma rápida introdução sobre o assunto.
O IntelliSense é uma ferramenta legal que vem embutida em editores como o SQL Server Management Studio (SSMS) e o Azure Data Studio (ADS). Ele é tipo o ajudante invisível que completa os comandos para você, aponta erros de sintaxe e, no geral, deixa a vida do desenvolvedor mais fácil.
Dependendo do que você faz nessas interfaces gráficas, o IntelliSense entra em ação. Por exemplo, quando você vai modificar uma procedure, ele pode aparecer:
Tendo essa base vamos ao nosso caso. Estávamos usando o SSMS 19.x e trabalhando num Azure SQL Database. Importante destacar o tipo de ambiente pois cada um pode ter suas particularidades.
Tentávamos modificar uma procedure pela interface usando o botão “Modify” no SSMS ou ADS. Até aí tudo bem, mas quando o script aparecia na tela, o IntelliSense gerava uma sessão para buscar alguns dados (provavelmente para guardar em cache e validar a query). O problema era que cada uma dessas sessões consumia cerca 10% dos DTUs do cliente. Podemos observar o consumo constante com a WhoIsActive usando o @delta_interval=1:
Para cada usuário era uma nova sessão, tivemos acúmulos de 50% do consumo das DTUs. Não adiantava dar “KILL” nessas sessões, porque ele abria outra sessão no lugar. Até fechar a aba que gerou a query seguido do Kill não adiantava, as sessões só eram encerradas com o fechamento do SSMS ou ADS por completo.
Tentamos simular em outros ambientes, mas não conseguimos. Em outros ambientes já tivemos casos de planos de execução ruins causados pelo Job Manager, e conseguimos resolver atualizando umas estatísticas relacionadas ao problema. Porém, devido às limitações do ambiente não conseguimos seguir essa linha de raciocínio.
Parece que alguma particularidade do ambiente do cliente contribuía para o problema. Depois de alguns testes, chegamos a algumas conclusões:
- Desligando o IntelliSense não temos problema.
- O problema só acontecia nas versões 19.x do SSMS.
- Nas versões 18.x e anteriores, o problema não ocorria.
- Com o lançamento da versão 20.x do SSMS em 19/03 e a versão 1.48 do ADS, o problema não ocorreu novamente.
- Nas notas de atualizações do SSMS e ADS não encontramos bugs relacionados
Pelos testes acreditamos que alguma particularidade do ambiente contribuía para esse Bug do IntelliSense presente nas versões 19.x.
Com o SSMS atualizado, o cliente segue sem problemas. Se o problema voltar a acontecer, iremos coletar as informações necessárias e reportar pra Microsoft, assim podemos ter certeza se é realmente um Bug.
Para os aventureiros segue a query que o IntelliSense ficava agarrado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
( @_msparam_0 nvarchar(4000), @_msparam_1 nvarchar(4000), @_msparam_2 nvarchar(4000) ) SELECT clmns.name AS [Name], clmns.column_id AS [ID], clmns.is_nullable AS [Nullable], clmns.is_computed AS [Computed], CAST(ISNULL(cik.index_column_id, 0) AS bit) AS [InPrimaryKey], clmns.is_ansi_padded AS [AnsiPaddingStatus], CAST(clmns.is_rowguidcol AS bit) AS [RowGuidCol], CAST(ISNULL(cc.is_persisted, 0) AS bit) AS [IsPersisted], ISNULL(clmns.collation_name, N'') AS [Collation], CAST( ISNULL( ( select TOP 1 1 from sys.foreign_key_columns AS colfk where colfk.parent_column_id = clmns.column_id and colfk.parent_object_id = clmns.object_id ), 0 ) AS bit ) AS [IsForeignKey], clmns.is_identity AS [Identity], CAST(ISNULL(ic.seed_value, 0) AS numeric(38)) AS [IdentitySeedAsDecimal], CAST(ISNULL(ic.increment_value, 0) AS numeric(38)) AS [IdentityIncrementAsDecimal], ( case when clmns.default_object_id = 0 then N'' when d.parent_object_id > 0 then N'' else d.name end ) AS [Default], ( case when clmns.default_object_id = 0 then N'' when d.parent_object_id > 0 then N'' else schema_name(d.schema_id) end ) AS [DefaultSchema], ISNULL(dc.Name, N'') AS [DefaultConstraintName], ( case when clmns.rule_object_id = 0 then N'' else r.name end ) AS [Rule], ( case when clmns.rule_object_id = 0 then N'' else schema_name(r.schema_id) end ) AS [RuleSchema], CAST( ISNULL( COLUMNPROPERTY(clmns.object_id, clmns.name, N'IsDeterministic'), 0 ) AS bit ) AS [IsDeterministic], CAST( ISNULL( COLUMNPROPERTY(clmns.object_id, clmns.name, N'IsPrecise'), 0 ) AS bit ) AS [IsPrecise], ISNULL(ic.is_not_for_replication, 0) AS [NotForReplication], CAST( COLUMNPROPERTY( clmns.object_id, clmns.name, N'IsFulltextIndexed' ) AS bit ) AS [IsFullTextIndexed], CAST(clmns.encryption_type AS int) AS [EncryptionType], clmns.encryption_algorithm_name AS [EncryptionAlgorithm], clmns.column_encryption_key_id AS [ColumnEncryptionKeyID], ceks.name AS [ColumnEncryptionKeyName], CAST(clmns.is_filestream AS bit) AS [IsFileStream], CAST(clmns.is_sparse AS bit) AS [IsSparse], CAST(clmns.is_column_set AS bit) AS [IsColumnSet], usrt.name AS [DataType], s1clmns.name AS [DataTypeSchema], ISNULL(baset.name, N'') AS [SystemType], CAST( CASE WHEN baset.name IN (N'nchar', N'nvarchar') AND clmns.max_length <> -1 THEN clmns.max_length / 2 ELSE clmns.max_length END AS int ) AS [Length], CAST(clmns.precision AS int) AS [NumericPrecision], CAST(clmns.scale AS int) AS [NumericScale], ISNULL(xscclmns.name, N'') AS [XmlSchemaNamespace], ISNULL(s2clmns.name, N'') AS [XmlSchemaNamespaceSchema], ISNULL( ( case clmns.is_xml_document when 1 then 2 else 1 end ), 0 ) AS [XmlDocumentConstraint], CASE WHEN usrt.is_table_type = 1 THEN N'structured' ELSE N'' END AS [UserType], clmns.generated_always_type AS [GeneratedAlwaysType], CAST(clmns.is_hidden AS bit) AS [IsHidden], CAST(clmns.is_dropped_ledger_column AS bit) AS [IsDroppedLedgerColumn], CAST(clmns.is_masked AS bit) AS [IsMasked], CAST( CASE WHEN [sc].[label] IS NOT NULL or [sc].[label_id] IS NOT NULL or [sc].[information_type] IS NOT NULL or [sc].[information_type_id] IS NOT NULL or [sc].[rank] IS NOT NULL THEN 1 ELSE 0 END AS bit ) AS [IsClassified], ISNULL(clmns.graph_type, 0) AS [GraphType] FROM sys.tables AS tbl INNER JOIN sys.all_columns AS clmns ON clmns.object_id = tbl.object_id LEFT OUTER JOIN sys.indexes AS ik ON ik.object_id = clmns.object_id and 1 = ik.is_primary_key LEFT OUTER JOIN sys.index_columns AS cik ON cik.index_id = ik.index_id and cik.column_id = clmns.column_id and cik.object_id = clmns.object_id and 0 = cik.is_included_column LEFT OUTER JOIN sys.computed_columns AS cc ON cc.object_id = clmns.object_id and cc.column_id = clmns.column_id LEFT OUTER JOIN sys.identity_columns AS ic ON ic.object_id = clmns.object_id and ic.column_id = clmns.column_id LEFT OUTER JOIN sys.objects AS d ON d.object_id = clmns.default_object_id LEFT OUTER JOIN sys.default_constraints as dc ON clmns.default_object_id = dc.object_id LEFT OUTER JOIN sys.objects AS r ON r.object_id = clmns.rule_object_id LEFT OUTER JOIN sys.column_encryption_keys AS ceks ON ( ceks.column_encryption_key_id = clmns.column_encryption_key_id ) LEFT OUTER JOIN sys.types AS usrt ON usrt.user_type_id = clmns.user_type_id LEFT OUTER JOIN sys.schemas AS s1clmns ON s1clmns.schema_id = usrt.schema_id LEFT OUTER JOIN sys.types AS baset ON ( baset.user_type_id = clmns.system_type_id and baset.user_type_id = baset.system_type_id ) or ( (baset.system_type_id = clmns.system_type_id) and (baset.user_type_id = clmns.user_type_id) and (baset.is_user_defined = 0) and (baset.is_assembly_type = 1) ) LEFT OUTER JOIN sys.xml_schema_collections AS xscclmns ON xscclmns.xml_collection_id = clmns.xml_collection_id LEFT OUTER JOIN sys.schemas AS s2clmns ON s2clmns.schema_id = xscclmns.schema_id LEFT OUTER JOIN sys.sensitivity_classifications AS sc ON sc.major_id = clmns.object_id and sc.minor_id = clmns.column_id WHERE (clmns.name = @_msparam_0) and( ( tbl.name = @_msparam_1 and SCHEMA_NAME(tbl.schema_id) = @_msparam_2 ) ) |
Se alguém já passou por isso ou tem alguma sugestão sobre a causa, deixa aí nos comentários, será de grande ajuda!