Opeens “Service Unavailable” op een relatief nieuwe Report Server(?)

Is dit bij u aan de hand…

  • U heeft net een nagelnieuwe Report Server opgeleverd gekregen?
  • U maakt verbinding met de Report Server via een internet browser?
  • Dat gaat een paar weken goed, totdat opeens bij het openen van de report server wél de folders worden getoond, maar bij het openen van elk rapport de melding “Service Unavailable” veschijnt?

De waarschijnlijke oorzaak is dat de evaluatie-versie van Report Server is geïnstalleerd. Dit is te controleren door in de folder C:\Program Files\Microsoft SQL Server Reporting Services\SSRS\LogFiles\ (of de folder waar bij u Report Server is geïnstalleerd) het logbestand ReportingServicesService_[datum/tijd].log te openen en te scannen op meldingen.

… En ja … Betrek altijd alle logs van SQL Server bij uw analyses. Ze zijn écht uw beste vrienden.

Foutmelding bij deployen van kubussolutions naar een Microsoft SQL Server Analysis Server

Enige tijd geleden was ik bezig met het deployen van kubussolutions naar een Microsoft SQL Server Analysis Server. Bij het deployen, verscheen de volgende melding An error has occurred, please contact your administrator \nInternal error: An unexpected error occurred (file ‘pcserialize.cpp’, line 1535, function ‘ASDatabase::Serialize’)

“An unexpected error”; typisch Microsoft waar je dus niets mee kunt. Na lang onderzoek bleek de oorzaak eigenlijk heel simpel: in het security tabblad van de server properties waren geen administrators opgenomen; zelfs geen BUILTIN\Administrators. Blijkbaar is het een goed idee om in dat tabblad in ieder geval één account te vermelden. Zo dus…

Na het toevoegen van een administrator in dit tabblad, rolden de kubussolutions wél goed uit.

SQL Server databases – informatie over de restore van een backup

Regelmatig krijg ik de vraag: “Weet jij wanneer de backup van [deze] en [deze] database is gemaakt en wanneer die is teruggezet?”. Het antwoord is ‘ja’… en met deze bijdrage weet u het nu ook. Draai op de SQL Server waar de teruggezette database staat onderstaande query:

SELECT 
   [rs].[destination_database_name], 
   [rs].[restore_date], 
   [bs].[backup_start_date], 
   [bs].[backup_finish_date], 
   [bs].[database_name] as [source_database_name], 
   [bmf].[physical_device_name] as [backup_file_used_for_restore]
FROM msdb..restorehistory rs
INNER JOIN msdb..backupset bs ON [rs].[backup_set_id] = [bs].[backup_set_id]
INNER JOIN msdb..backupmediafamily bmf ON [bs].[media_set_id] = [bmf].[media_set_id] 
ORDER BY [rs].[restore_date] DESC

Zo wordt de gevraagde informatie gevonden.

Met dank aan Thomas LaRock die deze informatie in het Engels in deze bijdrage heeft gedeeld.

Opstarten SQL Server onder ‘Local Service’ account; niet doen!

Bij de installatie van SQL Server wordt de SQL Service onder een zogenaamd local service account gestart. Heel af en toe valt het mij op dat het service account waaronder onderdelen van SQL Server worden gestart, niet naar een domein account wordt gewijzigd. Het is verstandig om de onderdelen van SQL Server onder aparte (!) domein accounts te laten starten; dus een apart domein account voor de database engine, een apart domein account voor de analysis server, één voor de SQL Agent, etc.

Waarom zo moeilijk als het ook makkelijk kan?
Het gebruik van een domein account zorgt ervoor dat op de onderdelen van SQL Server eenvoudiger wijzigingen kunnen worden gemaakt. Afgelopen week kwam ik tegen dat SSAS kubussolutions niet naar een analysis server konden worden uitgerold, omdat er geen gebruik van een domein account werd gemaakt. Bij gebruik van het ‘local service account’ weet het systeem niet of er voldoende rechten zijn bij o.a. het uitrollen van de kubussolutions vanaf een andere machine.

Ok… maar aparte domein accounts voor elk onderdeel van SQL Server?
Hoeft niet, maar het maakt de rechtenstructuur wél duidelijker. Een voorbeeld: om kubussen bij te werken onder het service account waaronder de analysis server wordt gestart, dient dat service account wél leesrechten te hebben op de brondatabase van de database engine; óók wanneer de database engine onder hetzelfde domein account wordt gestart. Het is dan overzichtelijker en duidelijker om de analysis server met een eigen service account te starten.

Kan het nóg scherper / beter?
Ja, door gebruik te maken van “(group) managed service accounts”. Het is wat meer werk, maar heeft als voordeel dat deze accounts uitsluitend kunnen worden gebruikt door de machines die hier rechten voor hebben gekregen. Daarnaast heeft dit account geen wachtwoord, waardoor het account ook niet voor andere doeleinden kan worden gebruikt / misbruikt. Met dank aan mijn collega voor deze mooie aanvulling.

Dus gebruik domein (group) managed service accounts bij het starten van onderdelen van SQL Server in een netwerk met Active Directory als basis.

** SQL Server database compatibility level? Wat is dat? **

Regelmatig komen we databases tegen die op de laatste versie van Microsoft SQL Server draaien en toch niet van de laatste technieken en inzichten gebruik kunnen maken. Met de verhuizing van een SQL Server database naar een nieuwe machine met de laatste SQL Server versie is het nog niet klaar! Dat geldt ook wanneer de SQL Server wordt bijgewerkt met de laatste versie van SQL Server. De betrokken database(s) moet(en) nog worden bijgewerkt!

Microsoft die zorgt ervoor dat een nieuwere versie van SQL Server niet direct impact heeft op de databases die op de SQL Server draaien. Elke database heeft op database niveau in het tabblad ‘Options’ de instelling ‘Compatibility Level’. Deze instelling zorgt ervoor dat de database zich gedraagt zoals die op de ‘oude’ versie van SQL Server is aangemaakt. Dit voorkomt dat de database opeens onder andere performance issues geeft of query plannen maakt, waar de database helemaal niet geschikt voor is.

Wanneer wordt de instelling “Compatibility Level” dan gewijzigd?
Na uitgebreide tests! De best-practice is om een kopie van de database op een testmachine te plaatsen en eerst daar deze instelling naar de hoogste versie te brengen om vervolgens alle relevante query’s en andere processen op de database te testen. Vervolgens kunnen op deze database dan de nodige wijzigingen worden gemaakt (en opnieuw worden getest) om de wijzigingen vervolgens op een geschikt moment in productie te brengen en dan pas in productie de instelling ‘Compatibility Level’ te wijzigen.

Moet de instelling worden gewijzigd?
Nee, het moet niet, voor zolang Microsoft de huidige compatibility level ondersteunt. Of het verstandig is om het niet te doen, is een tweede. Ik kan mij voorstellen dat ook uw organisatie met de laatste inzichten en technieken wilt werken. Dan is het echt noodzakelijk om de tests uit te voeren en uiteindelijk de productie-database naar het juiste niveau te brengen.

Ransomware; ook uw SQL Server is (juist) niet veilig!

Ook uw SQL Server is niet veilig voor Ransomware. Sterker nog: uw data op de SQL Server is een heel mooi doel voor Ransomware. Wist u dat Windows een ingebouwde beveiliging heeft voor Ransomware? Dit heet “Controlled folder access”.

Waar vind ik het en hoe stel ik het in?
Deze optie kan direct vanuit de zoekbalk in het Start Menu worden gevonden. In de zoekbalk geeft u “Ransomware Protection” in. Vervolgens heeft u daar de mogelijkheid de optie ‘Aan’ te zetten. BELANGRIJK: deze optie staat niet voor niets standaard uit! Microsoft heeft hier heel goed over nagedacht omdat deze optie alleen ‘approved apps’ wijzigingen kunnen en mogen maken op de bestanden in de betrokken folder(s).

Het staat aan… en nu?
Nu worden alle folders met gebruikersbestanden van de ingelogde gebruiker (Documents, dus bijvoorbeeld) beschermd tegen ongeautoriseerde wijzigingen. Het is mogelijk om eigen folders toe te voegen.

Wijzigingen worden nu tegen gehouden… wat nu?
Als de applicatie die de wijziging maakt betrouwbaar is, kan deze applicatie aan ‘Add an allowed app’ worden toegevoegd, eventueel via ‘Block history’.

Super! Nu ben ik dus helemaal beschermd?
NEE!

Geijkte tips

  • Blijf scherp wanneer u online of met uw e-mailberichten bezig bent.
  • Houd uw software altijd up-to-date (inclusief Windows Update
  • Maak regelmatig backups van uw software en uw bestanden

Blijf altijd op uw hoede.

Dan nog dit…
– Werkt het voor Windows 10? Ja!
– Werkt het voor Windows 11? Ja!
– Werkt het ook voor Windows Server (2019)? Ja!

Belangrijk nieuws: Nieuwe backup formaat in SQL Server 2019, CU16; dit kan logshipping kapot maken!

Op 18 april is SQL Server CU16 door Microsoft vrijgegeven. Hier zit een zeer belangrijke wijziging in. Backups worden in een nieuw formaat opgeslagen; in het bijzonder de zogenaamde Transparent Data Encryption (TDE)-gecomprimeerde backups. TDE-gecomprimeerde backups die met deze cumulative update zijn gemaakt, kunnen niet worden teruggezet op SQL Server 2019 servers met een eerdere cumulative update. Andersom blijft het overigens wél werken (dus een gecomprimeerde backup op een eerdere CU van SQL Server 2019 kan wél op deze CU worden terug gezet).

Wanneer is dit belangrijk?
Deze informatie is belangrijk wanneer:

  • … u uw database met TDE heeft versleuteld, en …
  • … wanneer u LOG backups neemt met de optie WITH COMPRESSION óf wanneer de server standaard (default) op backups met WITH COMPRESSION nemen staat, en…
  • … u maakt van logshipping gebruik naar andere SQL Server 2019 servers, en …
  • … u voorziet eerst de primary database (de bron database) van SQL Server 2019 CU16, voordat u de secondary database (kopie-database) van CU16 voorziet

Wat betekent dit concreet voor u!
Wanneer uw bedrijf eerst de secondary server (waar de kopie-database dus staat) van de nieuwe cumulative update voorziet, dan zal SQL Server 2019 tijdens het voorzien van de update op de primary server (de server waar de brondatabase staat) een fail-over naar de secondary database doen. In deze strategie zit er vaak een aantal uur tussen de eerste update op de secondary server en de volgende update op de primary server zitten. De primary server wordt in dit geval de secondary, maar backups die op de secondary zijn gemaakt (en nu dus de primary zijn), zullen niet meer kunnen worden ingelezen, vanwege deze nieuwe feature.

Ik ga ervan uit dat u als database administrator voldoende op de hoogte bent van update-procedures binnen uw bedrijf. Mijn dringende advies is te kijken of deze situatie op u van toepassing is en op gepaste wijze uw SQL Server 2019 servers bij te werken.

Belangrijk: deze situatie geldt dus alleen voor configuraties die voldoen aan de vier punten onder het kopje: “Wanneer is dit belangrijk?”

Links en verwijzingen
Alle informatie over de cumulative update (KB5011644)
Gedeelte dat over de TDE-gecomprimeerde backup gaat

Met dank aan Brent Ozar

Onjuist instellen van LSBackup maakt secondary logship database kapot

Vandaag is er wat tijd door mij ingeruimd om het SQL Server proces van logshipping nog beter te begrijpen. De opdracht die ik mij gaf, was: “Maak de secondary logship database kapot door onjuist instellen van de LSBackup job op de primary database.”

Voor deze opdracht was er een demo-database ingericht met daarop een secondary database op basis van logshipping. De basis van logshipping is dat alle mutaties op de primary database (de ‘hoofddatabase’) in een zogenaamde transactielog wordt vastgelegd. Van deze transactielogs wordt periodiek een backup (SQL Server Agent LSBackup job). gemaakt (de standaard bij het opzetten van een logshipdatabase is 15 minuten). Vervolgens worden deze transactielogs naar de server gekopieerd (SQL Server Agent LSCopy job) waar de secondary database op basis van logshipping staat en worden deze transactielogs daar ingelezen (SQL Server Agent LSRestore job).

Eén van de situaties die zich het meest voordoet in een ‘gebroken logshipketen’ op de secondary database, is dat er transactielogs ‘verdwijnen’ door wat voor oorzaak dan ook. Mijn onderzoek richt zich op het ‘verdwijnen’ van transactielogs door de LSBackup job; bij het maken van de backups van transactielogs. Is dat mogelijk?? Het korte antwoord is ‘ja’! Hoe? Lees vooral verder!

In de job LSBackup (waarmee backups worden gemaakt van de transactielogs) zit een eigenschap die in het Engels “Delete files older than…” heet. Deze instelling speelt een cruciale rol in het geheel. Wanneer de retentieperiode (de periode waarin de transactielogbestanden moeten worden blijven bewaard) tekort wordt ingesteld, zullen transactielogs te snel worden verwijderd en verloren gaan. Wat is dan een goede retentieperiode? Dat hangt af van de frequentie waarin de SQL Server Agent Job LSCopy, de job die ervoor zorgt dat transactielogs naar de server met de secondary database op basis van logshipping worden gekopieerd, draait. De retentieperiode van de transactielogs in de LSBackup job moet in ieder geval langer zijn dan de frequentie waarin de LSCopy job draait. Wanneer dat niet het geval is, zullen transactielogs te vroeg verwijdert worden, waardoor de secondary database op basis van logshipping niet meer adequaat bijgewerkt kan worden.

Wie zit er aan mijn SQL Agent jobs?

De HiX Datawarehouse verversing van ChipSoft haalt de data uit een tijdelijk statische HiX secondary database op basis van logshipping op. Om de database statisch te maken, zorgt de software van HiX Datawarehouse voor de correcte afhandeling van de logship procedures. Het is voor gebruikers en beheerders nadrukkelijk niet de bedoeling om zélf de logship, met name de LSRestore job, in- of uit te schakelen. Maar… monitor dat maar eens! Microsoft SQL Server heeft daar zélf geen voorziening voor én het goede nieuws is dat deze voorziening eenvoudig zélf te schrijven is.

Functioneel
Wanneer een SQL Agent job in- of uitgeschakeld wordt, wordt dit in de tabel [dbo].[sysjobs] binnen de msdb database opgeslagen. Deze wijziging kunnen we afvangen en de wijziging kan in een aparte beheertabel worden vastgelegd.

Technisch
Met een AFTER UPDATE trigger op de tabel dbo.sysjobs binnen de msdb database wordt het mogelijk om de wijziging in de job status (ingeschakeld / uitgeschakeld) af te vangen. De gewijzigde informatie wordt vervolgens in een beheerdatabase / beheertabel opgeslagen, zodat hier later naar gekeken kan worden.

De code hiervoor …

USE msdb;
GO
CREATE TRIGGER [dbo].Alert_on_job_stat
   ON  [dbo].[sysjobs]
   AFTER UPDATE
AS 
BEGIN
DECLARE @old_status BIT, @new_status BIT, @job_name VARCHAR(1024)
 
SELECT @old_status = enabled FROM deleted
SELECT @new_status = enabled, @job_name = name FROM inserted
 
  IF(@old_status <> @new_status)
  BEGIN
    INSERT INTO [audit_database].dbo.[audit_tabel] (login_name, job_name, status_, datetime)
    VALUES(ORIGINAL_LOGIN(), @job_name, @new_status, GETDATE())
    
    DECLARE @body_content VARCHAR(1024) = '';
    SET @body_content = 'SQL job : ' + @job_name + ' has been ' + 
              CASE @new_status WHEN 1 THEN 'Enabled' ELSE 'Disabled' END 
              + ' @ ' + CAST(GETDATE() AS VARCHAR(30))

    /* Eventueel een e-mail sturen
    EXEC msdb.dbo.sp_send_dbmail  
    @profile_name = 'SQL Alert', 
    @recipients = 'beheerders@domeinnaam.com',  
    @subject = 'SQL job status Change Alert',  
    @body = @body_content
    */
    ;  
  END
END

Aandachtspunten:

  1. De database audit_database is een zelfgekozen naam en dient uiteraard te worden aangemaakt
  2. De tabelnaam is een zelfgekozen naam en dient uiteraard te worden aangemaakt
  3. E-mail is standaard door mij uitgeschakeld
  4. De e-mail instellingen moeten goed worden geconfigureerd
  5. Gebruikers die SQL Agent Jobs in en uit kunnen schakelen, dienen voldoende schrijfrechten op de audit database en audit tabel te krijgen

Met dank aan Jignesh Raiyani voor het idee en de uitwerking in het Engels; Link naar de webpagina

Veldlengtes (n)varchar en (n)char velden vs. query performance vs. server performance

Vandaag werd ik door mijn collega getriggerd om onderzoek te doen naar extreme lengtes in (n)varchar velden en de impact daarvan op performance. Welnu… riemen vast, want dit is belangrijk.

Management samenvatting
Een goede inrichting en adequate keuze van veldlengtes bij (n)varchar en (n)char velden draagt bij aan een optimaler query plan en daarmee aan een betere query en server performance in het algemeen.

Hoe zit dit precies
U kent het wel: “Laten we dat tekstveld alvast maar wat langer maken, zodat we ook in de toekomst deze velden niet zo snel hoeven te verlengen”. Die beslissing kan impact hebben op de query performance van uw query’s op deze velden. SQL Server kijkt bij het maken van het query plan (die SQL Server bij query’s gebruikt) naar de grootte van de tekstvelden die in de query wordt gebruikt. Op basis daarvan maakt SQL Server een inschatting van het benodigd geheugen. Hoe meer de veldlengte van het veld in de tabel afwijkt van de daadwerkelijke vulling des te meer geheugen zal SQL Server onnodig aan deze query toewijzen. Gelukkig zal SQL Server (2019) u hier in het Actual Query Plan op wijzen bij het meest linkse (SELECT) component. U vindt in het Actual Query Plan op dat component een geel driehoekje met een waarschuwing.

Eén en ander is met onderstaand TestLab verduidelijkt. Deze set met query’s helpt u hopelijk beter te begrijpen wat er precies fout gaat. Dit is overigens geen ‘incident’. De onjuiste bepaling van het benodigd geheugen zal continue fout gaan wanneer de betrokken tabel (of, indien dit zich in meerdere tabellen voordoet, betrokken tabellen) in een query wordt betrokken. Draaien er parallel meerdere query’s, dan zal dit potentieel onnodig veel resources op het geheugen van uw SQL Server gaan leggen; en geheugen is schaars en kostbaar.

Vragen staat vrij!


/*
Auteur : Mickel Reemer
Datum : 23 maart 2022
Thanks to : Erik Darling
Credits by : https://www.brentozar.com/archive/2017/02/memory-grants-data-size/
SQL Server : SQL Server 2019 Developer Edition

Doel van het script:
--------------------
Aantonen dat varchar die te groot zijn aangemaakt
oorzaak zijn excessive memory grants op een SQL Server
*/
-- Zet tijdstatistieken aan
SET STATISTICS TIME ON

-- Verwijder de testtabel wanneer deze al bestaat
DROP TABLE IF EXISTS dbo.MemoryGrants;

-- Maak de testtabel aan
CREATE TABLE dbo.MemoryGrants
(
ID INT PRIMARY KEY CLUSTERED,
Ordering INT,
Field1 VARCHAR(10),
);

-- Voeg de records in, in de tabel
INSERT dbo.MemoryGrants WITH (TABLOCK)
( ID, Ordering, Field1)
SELECT
c,
c % 1000,
REPLICATE('X', c * 10 % 10)
FROM (
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS c
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2
) x

-- Zet Actual Query Plan aan en draai de query hieronder
SELECT mg.ID, mg.Ordering, mg.Field1 --Returning a VARCHAR(10)
FROM dbo.MemoryGrants AS mg
ORDER BY mg.Ordering
GO
-- De doorlooptijd is 1751ms. De memory grant is 13MB

/*
Dat gaan we nu nog een keer testen, maar dan met varchar(8000)
*/

-- Verwijder de testtabel opnieuw wanneer deze bestaat
DROP TABLE IF EXISTS dbo.MemoryGrants;

-- Maak de testtabel aan
CREATE TABLE dbo.MemoryGrants
(
ID INT PRIMARY KEY CLUSTERED,
Ordering INT,
Field1 VARCHAR(8000), -- We gaan nu Field1 een lengte van 8000 geven
);

-- Voeg de records in, in de tabel
INSERT dbo.MemoryGrants WITH (TABLOCK)
( ID, Ordering, Field1)
SELECT
c,
c % 1000,
REPLICATE('X', c * 10 % 10)
FROM (
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS c
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2
) x

-- Zet Actual Query Plan aan en draai de query hieronder
SELECT mg.ID, mg.Ordering, mg.Field1 --Returning a VARCHAR(10)
FROM dbo.MemoryGrants AS mg
ORDER BY mg.Ordering
GO
-- De memory grant is 574MB. De doorlooptijd is 2706ms