J’ai récemment eu l’occasion de pouvoir effectuer un bench IO sur une plateforme SQL Server sous ESX avec un couple Datacore / NetApp au niveau du stockage.
Contexte technique:
DataCore SANMelody / NetApp:
Le but de la manœuvre est de déterminer le débit maximum que sera capable de tirer le sous système IO cible. L’architecture est la suivante:
2 salles techniques composées chacune de (
– 1 Contrôleur NetApp FAS 3210
– 1 tiroir T1 de 14 x 450 Gb SAS 15KRPM en RAIDDP. (11 disques data, 2 disques DP, 1 disque de spare).
– 1 tiroir T2 de 14 x 600 Gb SAS 15KRPM en RAIDDP. (11 disques data, 2 disques DP, 1 disque de spare).
– 1 tiroir T3 de 14 x 600 Gb SAS 15KRPM en RAIDDP. (11 disques data, 2 disques DP, 1 disque de spare).
– 1 contrôleur DataCore SANMelody 3.0.3.5, 16Gb cache.
– 1 volume Tiers1 pris sur le tiroir T1 raccordé directement à DataCore.
)
Soit:
SANMelody est un logiciel tournant sur Windows Server et qui émule un SAN en quelque sorte. Il s’intercale entre le SAN et le host, et présente aux machines clientes (en l’occurence ici les 2 ESX) des disques comme s’il s’agissait directement du SAN. Il est à la fois initiateur côté baie, et target côté host. Deux avantages principaux:
– Il réserve une grande partie de la mémoire de la machine Windows pour bufferiser les IOs en provenance des hosts. Il ajoute un niveau de cache supplémentaire ici de 16Gb par contrôleur ce qui n’est pas rien.
– Ensuite, il permet de répliquer les blocs vers un second contrôleur de manière synchrone pour assurer une redondance des données.
Il est clair que le cache utilisé par SANMelody n’est pas WAL compliant dans la mesure où celui-ci réserve ses buffers à partir de la mémoire présentée par windows. Par contre, dans la mesure où le contrôleur est répliqué en synchrone sur une autre salle, chacune étant alimentée de manière autonome, on limite les risques. Il faudrait que les deux salles tombent en même temps pour exposer des données à la corruption. D’ailleurs une option ‘Force Cache Write Through‘ existe au niveau de SANMelody, qui permet comme son nom l’indique de forcer les écritures sur le média (donc le contrôleur NetApp). Donc bon, dans l’absolu, avec une bonne stratégie de backup derrière, pourquoi pas…
Dans tous les cas, nous allons tester avec et sans cette option.
Les ESX:
Du côté de la VM, on ne peut pas vraiment faire mieux: les procs sont des core i7 Xeon 7560, 64 bits, VT compatibles. D’après la matrice de VMM [1], un guest en 64 bits sur une telle plateforme profitera des avantages suivants:
– Il s’exécute en ring 0, donc on est en CPU-direct (pas de translation binaire).
– L’hyperviseur utilisera la technologie EPT du core i7 pour décharger la gestion des page tables et de la mémoire par guest.
On n’utilisera pas VMFS mais directement du soft RDM, donc pas d’histoire d’alignement [2]. Et de toutes façons, dans la mesure où ESX est amené à disparaître au profit d’ESXi, on pourra difficilement vérifier cela à l’avenir puisqu’on n’aura plus de service console.
Le guest OS quant à lui sera un Windows 2008R2 64 bits avec 4vCPU, 4Gb de mémoire dans un premier temps, et SQL Server 2005 x64 Enterprise Edition.
Les deux ESX forment un cluster HA DRS. Nous avons testé de lancer une alimentation (1 million de singleton inserts) pendant une bascule vMotion pour valider la partie secours et on n’a observé qu’un overhead de 7% seulement.
SQLIO:
Pour tester le sous système IO complet, nous nous appuierons sur SQLIO. Quelques rappels sur les paramètres de base:
Usage: sqlio [options] [<filename>...]
[options] may include any of the following:
-k<R|W> kind of IO (R=reads, W=writes)
-t<threads> number of threads
-s<secs> number of seconds to run
-d<drv_A><drv_B>.. use same filename on each drive letter given
-R<drv_A/0>,<drv_B/1>.. raw drive letters/number for I/O
-f<stripe factor> stripe size in blocks, random, or sequential
-p[I]<cpu affinity> cpu number for affinity (0 based)(I=ideal)
-a[R[I]]<cpu mask> cpu mask for (R=roundrobin (I=ideal)) affinity
-o<#outstanding> depth to use for completion routines
-b<io size(KB)> IO block size in KB
-i<#IOs/run> number of IOs per IO run
-m<[C|S]><#sub-blks> do multi blk IO (C=copy, S=scatter/gather)
-L<[S|P][i|]> latencies from (S=system, P=processor) timer
-B<[N|Y|H|S]> set buffering (N=none, Y=all, H=hdwr, S=sfwr)
-S<#blocks> start I/Os #blocks into file
-v1.1.1 I/Os runs use same blocks, as in version 1.1.1
-F<paramfile> read parameters from <paramfile>
Defaults:
-kR -t1 -s30 -f64 -b2 -i64 -BN testfile.dat
Maximums:
-t (threads): 256
no. of files, includes -d & -R: 256
filename length: 256
Nous nous contenterons d’utiliser les paramètres suivants:
-k: type d’accès (lectures / écritures)
– s: durée du test en secondes.
– f: sequential / random.
– o: nombre d’outstandings IOs, le nombre d’I/Os en attente par thread. Dans la mesure où chaque worker et le Lazy Writer postent la grande majorité des IOs en asynchrone, il y a des chances qu’il y ait plus d’une IO asynchrone par thread à un instant donné. Nous choisirons de tester 64, 128 et 256 outstanding IOs par thread.
– b: taille de l’IO. Nous utiliserons toutes les tailles multiple de la page de 8K jusqu’à 256K, en accès séquentiel, et seulement 8k en aléatoire.
– L: indique que nous souhaitons récupérer les valeurs de latence remontées par le système.
– B: indique que nous ne souhaitons pas utiliser le cache NTFS (les fichiers de données et journaux sont ouverts en FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING)
– F: nous allons passer à sqlio un fichier de paramètres dans lequel nous indiquerons le nombre de threads et la taille et la localisation du fichier de test.
Différentes batteries de tests:
Nous allons tester trois configuration possibles:
BANC1: NetApp -> Datacore sans Force Cache Write Through -> ESX -> Guest OS
BANC2:- NetApp -> Datacore avec Force Cache Write Through -> ESX -> Guest OS
BANC3: NetApp -> ESX -> Guest OS
Le BANC1 sera l’utilisation nominale de la plateforme. Le BANC2 devra montrer le prix à payer pour être 100% WAL-compliant. Dans la dernière configuration, on souhaite mesurer ce qu’on perd potentiellement si on retire Datacore de l’équation.
Préparation des scripts:
Il a fallu se constituer un petit package de recueil et d’agrégation d’informations pour le bench. On a choisi de le préparer en fonction des IOs types envoyées par SQL Server:
- Lectures séquentielles de 8,32,64,128 et 256 K (read-ahead, ramp up prefetch, DBCC CHECKDB…)
- Écritures séquentielles de 8,32,64,128 et 256 K (Backup / restore, Alter index Rebuild, Bulk insert…)
- Lectures aléatoires de 8K: lectures types OLTP.
- Écritures aléatoires de 8K: écritures types OLTP.
Voici le contenu du fichier .bat utilisé:
sqlio -kR -s120 -o64 -fsequential -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o64 -fsequential -b32 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o64 -fsequential -b64 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o64 -fsequential -b128 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o64 -fsequential -b256 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o64 -frandom -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o64 -fsequential -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o64 -fsequential -b32 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o64 -fsequential -b64 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o64 -fsequential -b128 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o64 -fsequential -b256 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o64 -frandom -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o128 -fsequential -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o128 -fsequential -b32 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o128 -fsequential -b64 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o128 -fsequential -b128 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o128 -fsequential -b256 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o128 -frandom -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o128 -fsequential -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o128 -fsequential -b32 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o128 -fsequential -b64 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o128 -fsequential -b128 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o128 -fsequential -b256 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o128 -frandom -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o256 -fsequential -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o256 -fsequential -b32 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o256 -fsequential -b64 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o256 -fsequential -b128 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o256 -fsequential -b256 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kR -s120 -o256 -frandom -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o256 -fsequential -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o256 -fsequential -b32 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o256 -fsequential -b64 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o256 -fsequential -b128 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o256 -fsequential -b256 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt" sqlio -kW -s120 -o256 -frandom -b8 -BH -LS -F"C:\Program Files\SQLIO\my_param_file.txt"
En tout 72 minutes de bench, trois pavés avec 64, 128 et 256 outstanding IOs par thread. Le contenu du fichier de configuration my_param_file.txt :
e:\testfile.dat 4 0x0 51200
On utilisera donc 4 threads (1 par vCPU) sur un fichier de 50Gb. Le choix dans la taille du fichier de test est important car on souhaite tester le sous-système disque et principalement le back-end de la baie. Il faut donc créer un fichier qui ne tienne ni dans le cache du DATACORE ni dans le cache du contrôleur I/O de la baie, pour obliger celle-ci à déborder sur les disques. Le plus gros datamart de l’application faisant 50Gb, on n’a donc pas choisi cette taille par hasard. On lance donc le script et on s’en va boire un café…
C:\Program Files\SQLIO>benchSQL.bat > results.txt
Intégration des résultats en base:
Une fois le test terminé, il va falloir intégrer les résultats en base. Le script ci-dessous doit nous permettre de stocker le contenu du fichier results.txt dans une table et de l’afficher de manière simple et conviviale. Ce script est un dérivé de celui que propose Brent Ozar (blog|twitter) sur SQLServerPedia et utilise une vue sur le fichier resultat brut plutôt qu’une table intermédiaire.
create database SQLIO
GO
use SQLIO
GO
CREATE TABLE [dbo].[SQLIO_Import](
[RowID] [int] IDENTITY(1,1) NOT NULL,
[ParameterRowID] [int] NULL,
[ResultText] [varchar](max) NULL,
CONSTRAINT [PK_SQLIO_Import] PRIMARY KEY CLUSTERED
(
[RowID] ASC
))
GO
create view v_sqlimport
as
select
-- Test set #
row_number() over (order by ParameterRowID) 'Test set #',
-- SQLIO Version
(select substring(ResultText, 7, len(ResultText))
from sqlio_import inner0 where ResultText like 'sqlio v%'
and inner0.ParameterRowID = imp.ParameterRowID) 'SQLIO Version'
-- # of threads
,(select substring(ResultText, charindex('with',ResultText) + 5,
(charindex('threads',ResultText)-(charindex('with',ResultText)+5)))
from sqlio_import inner1 where ResultText like '%using mask%'
and inner1.ParameterRowID = imp.ParameterRowID) '# of worker threads'
-- Read or Write
,(select substring(ResultText, charindex('-k',ResultText) + 2, 1)
from sqlio_import inner2 where ResultText like '%>sqlio%'
and inner2.ParameterRowID = imp.ParameterRowID) 'Reads (R) | Writes (W)'
-- Duration (secs)
,(select substring(ResultText, charindex('-s',ResultText) + 2,
((charindex('-',ResultText,charindex('-s',ResultText)+2))-(charindex('-s',ResultText) + 2)))
from sqlio_import inner3 where ResultText like '%>sqlio%'
and inner3.ParameterRowID = imp.ParameterRowID) 'Duration (sec)'
-- I/O Size (Kb)
,(select substring(ResultText, charindex('-b',ResultText) + 2,
((charindex('-',ResultText,charindex('-b',ResultText)+2))-(charindex('-b',ResultText) + 2)))
from sqlio_import inner4 where ResultText like '%>sqlio%'
and inner4.ParameterRowID = imp.ParameterRowID) 'I/O Size (Kb)'
-- I/O Patterns
,(select substring(ResultText, charindex('-f',ResultText) + 2,
((charindex('-',ResultText,charindex('-f',ResultText)+2))-(charindex('-f',ResultText) + 2)))
from sqlio_import inner4 where ResultText like '%>sqlio%'
and inner4.ParameterRowID = imp.ParameterRowID) 'I/O Pattern (Random | sequential)'
-- # outstanding I/Os
,(select substring(ResultText, charindex('-o',ResultText) + 2,
((charindex('-',ResultText,charindex('-o',ResultText)+2))-(charindex('-o',ResultText) + 2)))
from sqlio_import inner4 where ResultText like '%>sqlio%'
and inner4.ParameterRowID = imp.ParameterRowID) 'Outstanding I/Os per thread'
-- File Size (Mb)
,(select substring(ResultText, charindex('size:',ResultText) + 6,
((charindex('MB',ResultText,charindex('size:',ResultText)+6))-(charindex('size:',ResultText) + 6)))
from sqlio_import inner4 where ResultText like 'using specified size:%'
and inner4.ParameterRowID = imp.ParameterRowID) 'Target File Size (Mb)'
-- IOPS
,(select substring(ResultText, charindex('IOs/sec:',ResultText) + 8,
((len(ResultText)-(charindex('IOs/sec:',ResultText) + 7))))
from sqlio_import inner4 where ResultText like 'IOs/sec:%'
and inner4.ParameterRowID = imp.ParameterRowID) 'IOPS'
-- MBPS
,(select substring(ResultText, charindex('MBs/sec:',ResultText) + 8,
((len(ResultText)-(charindex('MBs/sec:',ResultText) + 7))))
from sqlio_import inner4 where ResultText like 'MBs/sec:%'
and inner4.ParameterRowID = imp.ParameterRowID) 'MBPS'
-- Min Latency
,(select substring(ResultText, charindex('Min_Latency(ms):',ResultText) + 17,
((len(ResultText)-(charindex('Min_Latency(ms):',ResultText) + 16))))
from sqlio_import inner4 where ResultText like 'Min_Latency(ms):%'
and inner4.ParameterRowID = imp.ParameterRowID) 'Min Latency (ms)'
-- Avg Latency
,(select substring(ResultText, charindex('Avg_Latency(ms):',ResultText) + 17,
((len(ResultText)-(charindex('Avg_Latency(ms):',ResultText) + 16))))
from sqlio_import inner4 where ResultText like 'Avg_Latency(ms):%'
and inner4.ParameterRowID = imp.ParameterRowID) 'Avg Latency (ms)'
-- Max Latency
,(select substring(ResultText, charindex('Max_Latency(ms):',ResultText) + 17,
((len(ResultText)-(charindex('Max_Latency(ms):',ResultText) + 16))))
from sqlio_import inner4 where ResultText like 'Max_Latency(ms):%'
and inner4.ParameterRowID = imp.ParameterRowID) 'Max Latency (ms)'
FROM dbo.sqlio_import imp
where ParameterRowID is not null
GROUP BY ParameterRowID
GO
print 'Please import data now and then run:
-- 1)
UPDATE dbo.sqlio_import
SET parameterrowid = (SELECT TOP 1 rowid
FROM dbo.sqlio_import parm
WHERE parm.resulttext LIKE ''%>sqlio %''
AND parm.rowid <= upd.rowid
ORDER BY rowid DESC)
FROM dbo.sqlio_import upd
GO'
print '
--2)
select * from v_sqlimport order by MBPS desc, IOPS desc
GO'
A cette étape, il ne reste plus qu’à importer le fichier results.txt avec SSIS (Database -> Tasks -> Import Data…). Il faut penser notamment à indiquer que le type de données en entrée est du DT_TEXT:
Puis à sélectionner la table SQLIO_Import et colonne ResultText comme réceptacles du fichier results.txt:
Une fois l’import du fichier terminé, il ne reste plus qu’à exécuter les tâches de post-import:
UPDATE dbo.sqlio_import SET parameterrowid = (SELECT TOP 1 rowid FROM dbo.sqlio_import parm WHERE parm.resulttext LIKE '%>sqlio %' AND parm.rowid <= upd.rowid ORDER BY rowid DESC) FROM dbo.sqlio_import upd GO (834 ligne(s) affectée(s))
Puis appeler la vue:
select * from v_sqlimport order by MBPS desc, IOPS desc
GO
Résultats
Légende:
– R8random128: reads 8K random 128 outstanding IOs
– R8sequential128: reads 8K sequential 128 outstanding IOs
– W8random128: writes 8K random 128 outstanding IOs
– etc…
- BANC1 vs BANC2: pas besoin de photo finish pour les départager:
680Mb/s sur du read-ahead, on peut difficilement battre un tel résultat à part peut être avec des SSD, qui eux offrent en plus un stockage résilient.
- BANC1 vs BANC3: avec ou sans Datacore ? c’est le même constat.
Je dois avouer que j’ai été très surpris des performances offertes par la solution de Datacore. Ca peut être une arme terrible pour écraser tous les problèmes de latence IO dans des environnements virtualisés… En attendant la globalisation des SSD, et avec sa solution de réplication de blocs, Datacore a encore de beaux jours devant lui.
A+. David B.
Références:
[1]: Virtual Machines Execution Modes in VSphere 4.0, Nikhil Bhatia, VMWare Corp
[2]: Storage Block Alignment with VMWare Virtual Infrastructure, Tim Coste, juillet 2007, TR-3593
Continuez votre lecture sur le blog :
- Règles d’installation de base (épisode 1) (David Baffaleuf) [SQL ServerVintage]
- Retrouver la requête à l’origine d’une erreur 8623 “The query processor ran out of internal resources and could not produce a query plan” (David Baffaleuf) [SQL Server]
- Consistence des écritures avec SATA (David Baffaleuf) [Operating SystemSQL Server]
- MySQL et les tables temporaires internes (Benjamin VESAN) [MySQL]
- Sessions consommatrices dans tempdb (David Baffaleuf) [SQL ServerVintage]
Salut David,
Encore un billet très intéressant.
Je pense que tu as inversé les titres Total IOPS et Total Débit / ms dans la partie résultats.
Maintenant je peux comparer mes résultats de tests sur des cartes SSD et cartes HP IO Fusion avec ta configuration. Dans mon test par contre il est vrai que j’ai enlevé tous les résultats dont la latence moyenne était supérieure à 20ms ..
++
Merci Mike pour la relecture, j’ai modifié l’article en conséquence.
le GROS pb de datacore est la gestion du cache en cas de crash …. pour ma part je ne mettrai jamais mes données entre les mains d’un Microsoft Windows et d’un cache mémoire qui à tout moment peut corrompre voire perdre définitivement mes données … je préfère dormir tranquille
pour info c’est du vécu chez 2 de mes clients avec pertes de données massives …. et volontée de ces derniers de repasser sur de vraies baies SAN dès que possible !!!
d’accord avec toi
mais quand on mets tout bout à bout pour sécuriser l’ensemble de manière sérieuse ça fait cher le SAN … avec toute la “quincaillerie” qu’il faut mettre en œuvre et assumer en exploitation …
autre point, en cas de contentieux, qui va assumer la responsabilité des pertes engendrées ? datacore ? microsoft ? le fabricant de switchs ? le fabricant de baies ? l’intégrateur ? le client ? ça fait beaucoup de monde dans la boucle et de belles parties de ping pong en vue !!! mieux que les JO 😉
pour info mes clients avaient tous les deux des solutions datacore redondées avec tout le toutim et ça n’a pas empêché une perte totale des données hébergées et obligation de rejouer une restitution des bandes de backup
Bonjour à tous,
twx29, que recommandes-tu dans ce cas, face aux pb d’i/o ? La SSD n’est elle pas encore à un coût trop élevée ?
Bonjour,
Je ne suis pas d’accord avec twx29 :
Les données du cache du contrôleur datacore 1 sont mirrorées vers le cache du contrôleur datacore 2 avant d’envoyer au serveur applicatif la confirmation de l’écriture “sur disque” (en réalité, doublée sur de la mémoire non persistente). Ainsi en cas de crash d’un controleur Datacore (qui bien entendu sont dans des salles techniques différentes et alimentés électriquement par des arrivées différentes), les données mise en cache sont présentes sur le controleur datacore 2 qui prend le relais, dépile tout de suite tout son cache sur disque puis le désactive le temps que le deuxième contrôleur datacore soit de nouveau up. de cette manière la perte de données ne doit pas survenir.(http://www.datacore.com/Software/Features/List-of-Features/High-Speed-Caching.aspx)
Bonjour,
Je ne suis pas d’accord avec twx29, j’installe du Datacore depuis des années chez des clients et ceux depuis la version 6 de SANSymphony. Je n’ai jamais perdu le moindre octet. Question de design et de respect des règle de l’art notamment la mise en place d’un onduleur. Avec deux Racks dans deux salles séparées, je ne sais pas comment TWX29 as-tu pu perdre des données. Pour les sceptiques, c’est simple à télécharger et tester. Pour ceux qui croient que le 21/12/12 est la fin du monde, sachez que le cache en écriture peut être désactivé unitairement par vdisk pour laisser que le cache en lecture. Et même avec cache en écriture disable les perfs sont amélioirées car en générale on fait plus de read que de write. Pour info, la plus part de mes installations sont dans le milieu hospitalier donc critique.
Bonjour,
question de béotien mais quel est l’intérêt de Datacore dans l’architecture présentée plus haut (hormis l’apport de cache) versus un solution MetroCluster NetApp ?
Merci pour vos différents éclairages