vendredi 27 août 2010

Restaurer une DB en ayant perdu un backup intermédiaire

Voici une question soulevée il y a quelque-jours:
Est-il possible de restaurer une DB si l'un des backups intemédiaire est perdu?

Cette question n'était pas anodine, c'est un cas vécu par un client.
Comme elle a piqué mon intérêt, j'y ai consacré un peu de temps pour y fournir une réponse précise.

Réponse
Oui, si le backup mode est "full" et que l'on dispose de tous les backups de transaction log intermédiaires.
Comme quoi il peut être utile de ne pas détruire ses backups trop vite!

Le cas de figure
Une DB SqlServeur en backup mode "Full" est backupée tous les jours (un fichier par jour) et le transaction log est backupé plusieurs fois par jours (également un fichier par jour).
Dans le temps, nous obtenons donc la logique suivante:
  • T1: backup DB 1
  • T2: backup Log 1
  • T3: backup DB 2 Le backup perdu.
  • T4: backup log 2
  • T5: Crash... et il faut restaurer la DB.
Cas de restauration idéal:
Le cas idéal est bien évidement de restaurer le dernier backup connu (Backup DB 2 fait à T3) et d'appliquer le transaction log backup (Backup log 2 fait à T4).
Mais dans le cas qui nous concerne, "backup DB 2" a été corrompu (erreur disque).

Alternative de restauration:
Par change le "backup log 2" n'a pas été corrompu et comme "Backup DB 1" et "Backup log 1" sont à notre disposition, il est quand même possible de restorer la DB dans l'état du temps T4.
L'opération à conduire est la suivante:
  1. Restorer le backup DB 1
  2. Restorer le backup Log 1
  3. Restorer le backup Log 2
Pourquoi est-ce que cela marche?
En backup mode "full", seul les parties du transaction log backupée avec "Backup transaction" sont libérées.
Ces parties peuvent dont être réutilisées par Sql Serveur pour y enregistrer de nouvelles transaction.
En lisant entre les lignes, cela veut dire que s'il n'y a jamais de BACKUP TRANSACTION pour une DB configurée en backup mode "full", le transaction log va gonfler... gonfler... jusqu'a consommer tous l'espace de disponible sur le disque.
Lorsque l'on fait un backup de la DB, il y a normalement une opération de CHECKPOINT. Cette dernière reporte les modifications enregistrées dans le Log File sur le Data file.
Ensuite le data file est backupé.
En aucun cas, cette opération de CHECKPOINT (et backup DB) n'altère le log file (hormis l'inscription de l'opération de checkpoint ou de backup).
En conséquence, il n'y a pas de libération d'espace dans le transaction log.

C'est la prochaine opération "Backup Transaction" qui libère la partie devenue inactive du transaction log ("inactive" parce que backupée).
Donc, selon le cas de figure énoncé plus haut, l'opération "Backup log 2" a backupé toutes les transactions entre les temps T1 et T4 (même si une partie d'entre elles ont déjà été appliqués dans le Data file par des checkpoints).

Exemple à l'appui
Voici un petit exemple qui crée des entrées dans une table en insérant ci et là des operations de backups (tantot DB, tantot log) dans des fichiers différents.
Soit les opérations suivantes:
  • Insertion des records 1 à 100.000
  • Backup DB 1
  • Insertion des records 100.000 à 150.000
  • Backup Log 1
  • Insertion des records 150.001 à 200.000
  • Backup DB 2 Le backup supposé perdu.
  • Insertion des records 200.001 à 250.000
  • Backup Log 2

L'exemple constitué des scripts ci-dessous démontre qu'il est possible de récupérer la DB dans l'état final sans utiliser le backup intermédiaire "Backup DB 2".

Création de la DB de test
Attention, le backup mode doit être "full"
USE [master]
GO
CREATE Database PlayGround
GO
ALTER DATABASE [PlayGround] SET RECOVERY FULL WITH NO_WAIT
GO
use [PlayGround]
GO
Create Table dbo.ListInt (
  value int not null
)
GO
Alter table dbo.ListInt
Add Constraint PK_ListInt PRIMARY KEY ( value )
GO
-- Populate the test table with records.
--    if parameters are missing, just a a new record at the end of the table
--    if @StartValue = null select last value +1
--    if @EndValue = null select evaluated StartValue + 1CREATE PROCEDURE dbo.PopulateList
  @StartValue int,
  @EndValue int
AS  
  DECLARE @Position int
  if @StartValue is null 
    SELECT @Position = Coalesce( MAX( value )+1, 1) from dbo.ListInt
  else 
    SET @Position = @StartValue
    
  DECLARE @EndPosition int
  if @EndValue is null 
    SET @EndPosition = @Position + 1 -- will insert only one row
  else 
    SET @EndPosition = @EndValue
  
  -- Insert rows
  while( @Position <= @EndPosition ) 
  BEGIN
 insert dbo.ListInt ( value ) values ( @Position )
 Set @Position = @Position + 1
  END
GO

Remplissage et backup
Simulation des différentes opérations de backups
Use PlayGround
GO
-- prepare initial data (0-100.000) + backupexec dbo.PopulateList @StartValue = 1, @EndValue = 100000 
GO
BACKUP DATABASE PLAYGROUND to disk = 'c:\Backup1.bak'
GO
-- Add data (100.000-150.000) + Backup Log
exec dbo.PopulateList @StartValue = 100001, @EndValue = 150000
GO
BACKUP LOG PLAYGROUND to disk = 'c:\Backup1.log'
GO

-- Add Data (150.001-200.000) + BACKUP !!!!
exec dbo.PopulateList @StartValue = 150001, @EndValue = 200000
GO
BACKUP DATABASE PLAYGROUND to disk = 'c:\Backup2.bak'
GO
-- Add Data (200.001 - 250.000) + BACKUP LOG
exec dbo.PopulateList @StartValue = 200001, @EndValue = 250000
GO
BACKUP LOG PLAYGROUND to disk = 'c:\Backup2.log'
GO

-- Question:
--   Would it be possible to restore the database from 
--      Backup1.Bak + Backup1.log + Backup2.Log 
--   (because we lost Backup2.bak)

Restoration de la DB
Après avoir effacé la DB, le script suivant démontre qu'il est possible de la restaurer sans utiliser le "backup 2" de la base de donnée.
-- Some interesting read from the NET
--   SQL SERVER – Restore Database Backup using SQL Script (T-SQL)
--   http://blog.sqlauthority.com/2007/02/25/sql-server-restore-database-backup-using-sql-script-t-sql/
--
--   How to restore a SQL Server transaction log backup
--   Also explain how to recover multiple LOGs backuped to a file 
--     http://www.mssqltips.com/tutorial.asp?tutorial=111
--USE master
GO
-- Retreive Logical Filenames from data file
RESTORE FILELISTONLY 
FROM DISK = 'c:\Backup1.bak'
GO

-- Restore Initial backup (NORECOVERY)
RESTORE DATABASE PlayGround 
FROM DISK = 'c:\Backup1.bak'
WITH MOVE 'PlayGround' TO 'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\PlayGround.mdf',
MOVE 'PlayGround_log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA\PlayGround_log.LDF',
NORECOVERY
GO

-- Restore the First Transaction Log
RESTORE LOG PlayGround FROM disk = 'c:\Backup1.log' WITH NORECOVERY
GO

-- Restore the SECOND Transaction Log
--    (we lost intermediate backup Backup1.Bak)
RESTORE LOG PlayGround FROM disk = 'c:\Backup2.log' WITH NORECOVERY
GO

-- Recover the Database
RESTORE DATABASE PlayGround WITH RECOVERY
GO

-- Set Multi-User
ALTER DATABASE PlayGround SET MULTI_USER
GO

-- Count number of records (250.000 expected)
use playGround
GO
select COUNT(*) from ListInt
GO

Références

jeudi 26 août 2010

WebServices et la gestion des sessions

Par nature, les WebServices sont Stateless.
Cependant, il est quelque-fois nécessaire de maintenir un état permettant, par exemple, de savoir si le client est passé par la méthode "connect" pour être authentifiée auprès de l'application.

A cette fin, l'usage d'une session ASP.Net dans un Web Service peut s'avérer fort utile.
A lire:
Il est cependant regrettable de constater qu'il n'y a pas de solution "out-of-the-box".
Dans le cas le plus simple, il est nécessaire de modifier le client du Web Service pour qu'il gère les cookies.
Si cela est encore simple en .Net (mais s'oublie facilement), cette exigence ne pourrait pas être rencontrée facilement sous d'autres plateformes... et c'est sans compter sur coopération des différents acteurs impliqués dans le développement.


A noter que Dr Bob s'est également penché sur le problème... ce qui produit un l'excellent article ASP.NET Web Services and Sessions.

Petite question perso:
Après la lecture des articles, je me demande s'il n'est pas possible d'adresser directement la collection des HttpSessionState dans le WebService pour ajouter et retrouver ma session.

Une méthode connect (au passage obligé) retournerait alors directement la valeur du SessionID.
Cette même valeur devenant un paramètre obligatoire de toutes les autres méthodes du WebService au l'authentification/l'identification est requise.
Il serait alors possible à la méthode du WebService de récupéer directement le HttpSessionState correspondant.


Un peu de lecture autour des WebServices

mardi 10 août 2010

Sql Serveur - Delete avec jointure

Afin d'éviter les jointures, la façon simple et la plus courante d'éviter les jointures reste encore l'utilisation d'une sous requête (ex ci-dessous).
declare @CnkNr varchar( 10 )
select @CnkNr = '1766625'

delete from Article.ExpDateLotNrs where ArtID in (
  select ArtID 
  from Article.Articles a 
  where a.CnkNr =  @CnkNr
)

L'utilisation d'une sous-requête (ex ci-dessus) est peu efficace si le résultat de la sous-requête retourne un ensemble de donnée important.
Dans d'autres cas, il peut être nécessaire d'utiliser une jointure sur plusieurs tables avant de connaître les enregistrements à effacer.
Il est alors nécessaire d'effectuer une opération "delete" sur base d'une jointure.

Tout comme l'update avec jointure, la syntaxe du Delete avec jointure est un peu particulière:
declare @CnkNr varchar( 10 )
select @CnkNr = '1766625'

delete from Article.ExpDateLotNrs 
FROM Article.ExpDateLotNrs L
inner join Article.Articles a on a.ArtID = l.ArtID and a.CnkNr =  @CnkNr

mercredi 14 juillet 2010

La programmation n'est pas une science exacte

Préambule
Voici un petit article axé sur la programmation et les mathématiques.
Un chapitre aborde également l'importance de l'implication personnelle dans la bonne conduite de projet d'envergure (raison pour laquelle cet article apparait également dans la section société-psycho)

La programmation n'est pas une science exacte
Beaucoup de néophytes et de développeurs pensent que la programmation se résume à (je paraphrase)  "l'ordinateur fait exactement ce qu'on lui demande".
Par conséquent, si on lui demande d'afficher cela à l'écran, il devrait le faire... s'il ne le fait pas ou pas bien, c'est le développeur qui est en cause.
Je partage cet avis, en écartant les problèmes de complexité (1*) et le cas des developpeurs brouillons (2*)... à la condition de l'appliquer à de petites portions d'un logiciel.
1*) car certains problèmes le sont vraiment... même pour des choses simples en apparences.
2*) car il y en a beaucoup dans notre profession.

A plus grande échelle, où les développements impliquent beaucoup de développeurs, beaucoup de code, beaucoup de fonctionnalités entre-croisées, la "stabilité d'un développement" est tout aussi imprévisible que la stabilité de "grosses infrastructures".

Même si certains responsables prétendent le contraire (et même si "les apparences" leur donnent raison... pour ce qu'ils veulent bien révéler), les grands systèmes informatiques (logiciel et infrastructure) sont instables.
Dans les faits, ces systèmes ne doivent leur stabilité qu'à la vigilance de tous les intervenants qui y sont impliqués. C'est la raison pour laquelle les systèmes informatiques (developpement et infrastructure) sont accompagnés de "service de maintenance".


Pourquoi le développement n'est pas une science exacte (1*)
En écartant les problèmes humains du sujet (mauvaise analyse, mauvais développeur), un système informatique n'est pas un système exacte.

En reprenant les propos de Joseph Sifakis (2*), détenteur du prix Turing, le comportement des systèmes informatiques ne peut pas être décrit avec des équations dites "linéaires" comme on le fait avec des circuits électriques. Les systèmes électriques peuvent être réduits à des systèmes d'équations relativement "élémentaires" (les connaisseurs pardonnerons cette simplification un peu extrême).

Il y a 35 ans, Joseph Sifakis pensait pouvoir modéliser un système informatique comme on le fait pour des systèmes électromécaniques. Pour lui, il est un fait aujourd'hui, que cela n'est pas possible pour les systèmes informatiques (réseaux, microprocesseur) . Après deux ans d'étude, cette modélisation linéaire ne s'applique même pas pour un bit en mémoire.
Du coup, puisque l'informatique ne peut être modélisée à l'aide d'équation "linéaire", elle échappe totalement à la prédictibilité... Au contraire de la physique traditionnelle, où il est possible de prévoir l'état futur à partir de l'état actuel.

Lorsque l'on observe globalement les systèmes informatiques, leurs constructions s'effectuent donc de façon empirique.
L'informatique d'aujourd'hui s'apparente à la construction des cathédrales du moyen âge (empirique elles aussi). Les cathédrales étaient montées pierre après pierre, avec savoir faire, mais sans les connaissances exactes de lois de la physique s'y appliquant... sans réelle prédictibilité.
Lorsque l'on prépare un nouveau processeur ou une nouvelle plateforme (web ou non), il n'est donc jamais possible d'avoir des garanties sur le résultat final.
Jusqu'à 30% des gros projets informatiques actuels échouent complètement avant leur achèvement.

1*: Les propos de Mr Joseph Sifakis proviennent du Science et Vie n° 1102 de Juillet 2009, page 146.
2*: Joseph Sifakis est informaticien, directeur de recherche au CNRS, fondateur du laboratoire Verimag (systèmes embarqués). En 2007, il a reçu le prix Turing, la plus haute récompense internationale scientifique en informatique.

Développement de logiciel d'envergure
Ce qui s'applique aux grands systèmes informatiques s'applique bien évidemment aux grands développements. La conception et modification de grands logiciels.
Le développement s'appuie sur ce procédé d'assemblage de briques (idée communément admise) soit dans le programme lui même par la méthode de programmation (losely coupled application), soit par les briques logicielles utilisées (serveur Web, serveur DB, Application Server, composants pour l'environnement de développement, outils divers créés pour le support au développement, etc).
Le développement, au sens général du terme, est donc empirique et s'appuie principalement sur le savoir faire (généralement épaulé par des techniques).
Nul aujourd'hui n'est capable d'assurer la réussite "à coup sur" d'un développement si l'on utilise tel ou tel technique de développement... il reste encore beaucoup de "chaos" en jeu pour faire aboutir un produit

La coercition est indispensable pour l'aboutissement d'un projet
Comme précisé au paravent (et c'est une conviction personnelle), la stabilité d'un gros développement, sa mise en production et sa maintenance dépend intimement du niveau d'implication de ses différents acteurs (concepteurs, développeurs, product owner, testeurs, services de maintenances et support).

Il est important, et beaucoup le perdent souvent de vue, qu'ils agissent tous en coercition pour faire aboutir la plateforme/produit/logiciel.
Seul le "bien du système" dans son ensemble, et cela incluant aussi le bien des acteurs, est l'objectif défendable à atteindre.

Il est malheureusement courant que les intérêts personnels d'un des acteurs prennent le pas sur les intérêts et la finalité du projet.
Suivant sa position de pouvoir (ou celui qu'il peut acquérir), cet élément perturbateur peut avoir un impact important au point de déstabiliser ou ruiner entièrement un projet.
Hormis les guerres de pouvoir qui usent inutilement l'énergie des intervenants (et n'apporte rien de positif au système), la démotivation, dont les sources sont variées mais souvent dépendantes de l'esprit de coercition, est probablement le pire des dangers pouvant déstabiliser la bonne conduite d'un projet.

La démotivation, bien qu'en apparence idéal à la coercition (puisque plus rien n'est discuté), engendre un manque d'implication, une dangereuse diminution de qualité pour le produit par manque de réactivité (les détections de problèmes potentiels étant passé sous silence par "ras-le-bol" ou crainte des conséquences).

Coercition et implication personnelle
La stabilité d'un système informatique est basé sur l'implication personnelle des intervenant.
l'implication personnelle dans un esprit de coercition est essentiel et seul le "bien du système" est le but valable a atteindre. Agir ensemble pour le bien du système c'est inévitablement agir pour son propre bien.
L'implication personnelle n'est par contre pas synonyme de "répondre au doigt et à l'oeil" mais plutôt d'appliquer un sens critique et préventif pour prévenir les problèmes.
Ne pas accepter ou se jeter tête baissée dans des situations/choix dont on sait pertinemment qu'ils représenterons des problèmes à court et a moyen terme.
Pour résumer: agir pour le bien du système.

Et là encore, l'esprit de coercition est important car hiérarchiquement, la personne levant/identifiant un problème (généralement plus bas dans l'échelle hiérarchique) et la personne à qui elle est reportée (plus haut dans l'échelle de décision) doivent agir de concert, en coercition, pour le bien du système dans son ensemble.


mardi 6 juillet 2010

Histoire de Linux

Voici, en référence, des vidéos publiées sur DailyMotion relatant l'histoire de Linux.
Un long reportage Arte de 50 minutes on l'on interview les principaux acteurs tels que Linus Torvalds et Richard Stallman.





lundi 5 juillet 2010

Snippet Compiler et LinQ

Qu'est-ce que Snippet Compiler
Snippet compiler est un mini-environnement .Net (gratuit) permettant d'écrire et tester rapidement des petits bouts de code C#.
Il n'est effectivement pas commode de lancer un environnement Visual Studio complet pour tester quelques lignes de code... à la longue, c'est pesant, surtout si l'on est pressé.
C'est là que Snipped Compiler se montre à la fois utile, véloce et simple d'emploi (ses fonctions helpers, tel que WL(), permettant d'interagir facilement avec la console).
J'en suis devenu un inconditionnel. 

Utilisation avec LinQ
Il y a peu, je voulais tester des résolutions de problèmes combinatoires avec LinQ.
Je démarre donc Snippet Compiler et m'aperçoit rapidement qu'il ne veux pas entendre parler de LinQ, ni de Enumerable.
La solution est plutôt simple une fois qu'on la connait;
Il faut ajouter une référence (via le menu Tools | References) sur System.Core.dll :-)

vendredi 25 juin 2010

Common Table Expression (CTE)

Les CTE (common Table Expression) peuvent être vu comme des results set temporaires.
Les CTE sont plus facile à utiliser que les tables temporaires et permettent une utilisation récursive (voir article Recursive Queries using CTE sur MSDN).
La durée de vie d'une CTE est limitée à l'exécution de la requête.
Pour plus d'information, voir l'article Using Common Table Expressions sur MSDN.

Les CTE deviennent utiles lorsque l'on doit appliquer une where clause sur un champ du query dont la valeur est non deterministe.
A titre d'exemple, un query dont le champs txt varchar(80) est calculé via une fonction utilisant un curseur sql (cela peut arriver si l'on doit faire des manipulations complexes sur les données pour composer le texte).
Si l'on désire n'afficher que lignes ayant plus d'un caractère, un select standard ne peut pas convenir.

Exemple:

En déclarant la fonction suivante (utilisée dans l'exemple ci-après):
Create function dbo.ChangeText( @txtIn varchar(50) )
returns Varchar(50) 
AS
BEGIN
  Return @TxtIn + '~Test~'
END

Il n'est pas possible d'exécuté la requête suivante:
select NameFr, dbo.ChangeText( NameFr ) as txt
FROM Apb.COUNTRY
where LEN( txt ) > 1

Erreur Sql:
Msg 207, Level 16, State 1, Line 3
Invalid column name 'txt'.

Par contre, l'usage d'un Common Table Expression d'atteindre l'objectif en stockant temporairement le dataset dans cte_Names.
Par la suite, le contenu de cte_Names est utilisé pour atteindre le résultat souhaité (à l'aide d'une seconde requête sql).
with cte_Names ( NameFr, txt )
AS (
   select NameFr, dbo.ChangeText( NameFr )
   FROM Apb.COUNTRY)
  
select NameFr, txt
FROM cte_Names
where LEN( txt ) > 1

Plus d'information