Coups de cache

J’ai un export assez lourd qui s’exécute une fois par semaine, le soir : une 60aine de tables à exporter en totalité au format SQL (des tas de SELECTION VERS TABLEAU, moulinage texte, écriture fichiers).

L’an dernier le serveur mac est passé en 64 bits, on lui a octroyé 8Go de mémoire (data de ~20Go). J’ai remarqué depuis que, quand ce traitement s’exécute, le serveur me fait régulièrement des “coups de cache” : affichage de la roue multicolore sur le serveur, coté client tout est quasi figé ; après quoi ça repart, tout gaillard… jusqu’au coup de cache suivant. Le profil du cache correspondant figure ci-dessous, la partie de droite étant ce qui se produit pendant l’export :


Un parfait relief de cuesta : une pente douce grimpante jusqu’au sommet suivi d’une brusque falaise tombante, et on recommence, ce que j’interprète ainsi :

  • montée douce = les enregistrements lus pour être exportés s’empilent petit à petit dans le cache
  • sommet = y’a plus de place dans le cache
  • falaise = 4D a fait du ménage
    Les roues multicolores se produisant à chaque sommet.

Désolé pour les explications un peu longues, mais je voudrais d’une part être sûr de mon interprétation, et d’autre part je ne suis pas sûr de la bonne façon de traiter le problème : comme le traitement ne fait que de la lecture d’enregistrements constitués essentiellement de champs scalaires, je pensais jouer sur AJUSTER PRIORITE CACHE TABLE, en mettant la priorité au maximum : est-ce la bonne solution ?

Bonsoir Arnaud,

Tu peux lancer la procédure stockée de 4D_Info_Report pour mesurer l’utilisation du cache et de la mémoire dans un intervalle avant et pendant l’export, pour avoir trace des valeurs courantes dans des rapports.

Il faudrait observer le comportement du Server au moment de cet export, et voir si par exemple la mémoire vive est bien disponible en quantité avant: si ce n’est pas le cas, un ‘purge’ peut être exécuté avant to export via le Terminal, ou alors via le plugin de Miyako : https://github.com/miyako/4d-plugin-purge

Dans une discussion précédente sur l’ancien forum, j’avais suggéré de voir l’impact de purge sur les opérations qui vont avoir un fort impact sur la mémoire disponible pendant certaines opérations de traitement de fichiers, par exemple dans le cas de la sauvegarde :
Cette observation sur la consommation de mémoire disponible par la sauvegarde était bien illustré dans la présentation vidéo faite par le usergroup 4DMethods: https://youtu.be/gr4nmZRetOA
(A regarder depuis la minute 48:15)
https://4dmethod.com/2014/12/30/jan-7-meeting-live-stream/

C’est une video de 2014, et je n’ai pas essayé récemment si cela a toujours un impact dans des versions récentes de 4D Server et de macOS.

Sinon, il faudra probablement faire si possible un REDEMARRER 4D avant cet export, pour partir d’un Cache vide.

Cordialement,
Thomas

Bonjour Thomas,
j’aurais peut-être dû préciser que c’est en v17r4 64 bits. La vidéo en question parle de v13, à l’époque c’était encore l’ancien cache “à 4 cases”, non ? Là je suis avec le nouveau cache : quand on nous l’a présenté, on voyait que 4D s’attribuait des “petits bouts” de mémoire, je supposais donc que les purges étaient beaucoup plus fluides qu’avec l’ancien cache et ses grands espaces mémoire à nettoyer.

Elle colle au profil en copie ci-dessus : au fur et à mesure que l’export progresse, le cache se rempli doucement (pente peu inclinée), puis quand on arrive au sommet (7,81Go) :

  • serveur = roue multicolore, fenêtre administration figée, le dock affiche “forcer à quitter”
  • coté client, 4D est quasi figé
    Ce blocage doit durer 20-30 secondes au moins. Quand enfin il cesse, les activités reprennent leur cours normal, le profil de cache a fait la chute brutale qu’on voit sur le graphique et se remet tranquillement à monter.

Ça n’est pas une solution trop viable, pour moi, que de devoir arrêter / relancer ce serveur qui est loin de ne faire que ça. J’espère que le “nouveau” cache offre une solution à ces à coups qui ne m’y oblige pas.

Juste pour notre information: quel type de disque possède ton serveur ? Plus ton cache est gros, plus long sera le temps nécessaire pour flusher l’intégralité du cache sur un disque surtout si celui-ci est lent (fragmenté, etc…)

C’est un SSD, sur serveur un mac pro alu de 2012, 12Go de ram.

12Go de RAM ce n’est pas énorme pour un serveur surtout si tu utilises déjà 8Go rien que pour le cache. Paradoxalement, il faudrait voir si en réduisant ta taille de cache, tu ne gagnes pas en fluidité. Le goulot d’étranglement se situe peut-être sur les entrées sorties entre ta RAM et tes disques.

Ça n’est pas l’intégralité du cache, qui est “flushée”, d’après le graphique, mais environ 1Go qui est libéré à chaque fois qu’un pic est atteint. 4D semble avoir du mal à se gérer ce “petit” ajustement. Le HDI fourni dans la doc m’a semblé sans intérêt dans mon cas, il n’y est pas question de volumétrie.

Je pourrais effectivement réduire l’alloc mémoire, on a déjà fait un truc dans ce sens. Mais il semble paradoxal de devoir allouer moins de cache pour aller plus vite.

Pour info, les flush sont programmés toutes les 20" en préférences.

J’ai le sentiment que ce nouveau cache a le potentiel pour gérer mon souci mais je ne sais pas trop dans quel sens lui caresser le poil. J’ai 2 pistes,
1/ prioriser les enregistrements comme évoqué plus haut
2/ faire des ECRIRE CACHE ( taille ) quand je vois que ça frôle le sommet

Faut juste faire attention macOS gère par ailleurs sa mémoire et il gérera donc lui même la mémoire virtuelle (il n’y a plus d’alerte pour dire qu’il n’y a plus de mémoire, d’un coté c’est bien, mais cela peut être gênant par ailleurs). Donc, s’il te met ton cache dans la mémoire virtuelle, je ne suis pas certain que 4D le sache lui.
Le cache c’est vraiment efficace s’il est complètement en mémoire physique réellement sinon…

Une copie écran du moniteur d’activité/4d server/mémoire/infos donne ça :
Capture d’écran 2020-06-03 à 10.51.23
Ce nombre de “blocages récents” à 17 à l’air propre à 4d serveur, les autres applis affichent 0 (du moins celles que j’ai regardées). La mémoire virtuelle me semble étonnante. Bon, après, tout ça n’est pas franchement mon domaine de savoir…

Reste que si j’ai 12Go de mémoire physique et que j’en alloue 8 au cache dans les prefs 4D, ça m’énerve un peu de me taper ces coups d’arrêt. Je vais lui coller la moitié bientôt, on verra bien…

Le comportement est exactement le même en réduisant la taille de moitié (8 -> 4Go) : un coup pour rien. En même temps, c’est plutôt rassurant, il serait paradoxal de ne pas utiliser la mémoire physique dont on dispose…

En suivant comment ça se passe avec Cache info, la façon dont 4D procède reflète exactement le profil sur le moniteur : le cache se rempli, quand il arrive à 0,99 d’occupation du cache (=saturation proche), il fait un “flush” qui libère 10% et on repart avec 0,90 d’occupation.

Si je m’immisce là dedans pour faire des flush avant lui, quand la saturation approche :

flush cache($taille)

le “prix à payer” d’un flush reste toujours ce blocage temporaire du serveur, et si je fais varier la valeur de $taille je ne constate pas de variation du temps du blocage. Du coup, j’ai pour le moment le sentiment que, tant qu’à faire du ménage, autant le faire en grand.

Peux tu faire une copie de la page mémoire des préférences de la base ?

Capture d’écran 2020-06-04 à 15.12.56
La copie est prise depuis le 4d client, d’où le non saisissable. Là c’est resté sur la valeur du test plus haut, 4000, mais “en vrai” je mets 8000.

As tu essayé le calcul du cache adaptatif ?
Si la base est ancienne, un test à faire est de restaurer les paramètres par défaut (sur le serveur) car la gestion des paramètres mémoire a évolué au fil des versions mais les réglages existants ont été conservé.
Tu n’est pas obligé d’enregistrer les changements, juste pour voir ce qui est calculé.
Sinon j’essayerais bien : cache adaptatif avec max à 4000

Non, pas essayé. Pour tout dire, je m’étais mis en tête que le cache adaptatif coché était le réglage “tout venant”, à utiliser quand une base est susceptible d’être déployée sur des configurations très hétérogènes en terme de mémoire, ou imprévisibles, si tu préfères. Apparemment j’ai encore compris de travers.

Le but étant que ton cache reste en mémoire physique réelle et ne part pas se balader en mémoire virtuelle. Mais le problème est que je ne sais pas s’il existe un paramétrage qui puisse obliger le système macOS à opérer de la sorte… Comme je te l’indiquais, il sait très bien gérer la mémoire infinie sans jamais rechigner, mais le revers de médaille c’est que du coup, il fait un peu ce qu’il veut (et parfois n’importe quoi ou du moins pas ce qu’on veut.)

Dans ton histoire, c’est surtout le temps TRÈS long du blocage qui est bizarre. Surtout si tu as un SSD (interne ?).

Ben non, tu as parfaitement compris.

Ce serveur de dev. est un Mac mini fin 2012 avec 16Go de mémoire réelle, SSD interne (écriture 300, lecture 500 MB/s) : ni mirobolant ni ridicule (du moins pour ce qu’on en fait).
Je ne me préoccupe pas trop de ce que fait le macOS, je pense qu’il faut chercher d’abord à faire au mieux avec le SGDB. Après, si vraiment ça veut pas, on verra.

Salut Maurice,
bon, tu me rassures, j’étais toujours dans la bonne religion. Bon, du coup, comme j’avais pris la peine de relire la doc avant de répondre à Vincent, je ne la trouve vraiment pas claire, limite contresens :
« Calcul du cache adaptatif non cochée"
La taille de mémoire cache que vous saisissez sera réservée pour la base 4D, quel que soit l’état des ressources de la machine. Ce paramétrage peut être utilisé dans certaines configurations spécifiques, ou lorsque la base est destinée à fonctionner sur des systèmes disparates en termes de mémoire. Dans la plupart des cas, le cache adaptatif est plus performant. »

Voilà, c’est fait : même sanction, exactement le même profil en dents de scie, avec le coup d’arrêt sur chaque pointe.

Je teste une solution qui ne me semble pas trop mal, apparemment :

  • mode 4D auto : quand le cache est à 99%, il libère 10%, ça prend ~ 6"
  • mode c’est moi que je décide : quand le cache est à 95%, je libère 50%, ça prend ~4"

J’ai toujours ces coups d’arrêt au moment du flush, quand 4D réorganise son cache. Mais d’une part je repars pour beaucoup plus longtemps avant le suivant, d’autre part on dirait que 4D préfère faire un gros trou de 50% plutôt qu’un petit de 10%.

Le code de test, si jamais ça intéresse (je ne fournis pas le bon gros data, par contre) :

  //cacheTests 
TRACE
ASSERT(Application type=4D Server)
C_COLLECTION($0)
$test_c:=New collection
$tropLong_l:=2000  //selection vers tableau de plus de 2 secondes = ecriture cache
$pas_l:=500
$flush_l:=(10^9)*2  //Go
$seuilFlush_l:=0.95  //mettre 1 pour laisser faire 4D
$prog_l:=UI_progressStart (-1;"";"test cache")
For ($j_l;1;Get last table number)
	If (Is table number valid($j_l))
		$table_p:=Table($j_l)
		$tableName_t:=Table name($j_l)
		$champ_p:=DB_fieldPtrByName ($j_l;"PK")  //pointeur clé primaire
		ASSERT(Ptr_isField ($champ_p))
		ALL RECORDS($table_p->)
		$debut_l:=1
		$end_l:=Records in selection($table_p->)
		While ($debut_l<$end_l)
			$fin_l:=$debut_l+($pas_l-1)
			If ($fin_l>$end_l)
				$fin_l:=$end_l
			End if 
			$i_l:=$fin_l\$pas_l
			UI_progressUpdate ($prog_l;Num_percent ($i_l;1;$end_l);$tableName_t)
			
			If (Cache info.usedMem/Cache info.maxMem>$seuilFlush_l)
				$info_o:=New object
				$info_o.ratioAvant:=Cache info.usedMem/Cache info.maxMem
				$ms_l:=Milliseconds
				FLUSH CACHE($flush_l)
				$ms_l:=Milliseconds-$ms_l
				$info_o.cacheInfoApres:=Cache info.objects.query("type = :1 AND table = :2";"data table";$tableName_t)
				$test_c.push(New object("flushProvoque";True;"duree";$ms_l))
			End if 
			
			$cacheInfoAvant_o:=OB Copy(Cache info)  //mesure cache avant saturation possible
			$ms_l:=Milliseconds
			SELECTION RANGE TO ARRAY($debut_l;$fin_l;$champ_p->;$dummy)
			$ms_l:=Milliseconds-$ms_l
			
			If ($ms_l>$tropLong_l)
				$info_o:=New object
				$info_o.ratioAvant:=$cacheInfoAvant_o.usedMem/$cacheInfoAvant_o.maxMem
				$info_o.cacheInfoAvant:=$cacheInfoAvant_o.objects.query("type = :1 AND table = :2";"data table";$tableName_t)
				$info_o.ratioApres:=Cache info.usedMem/Cache info.maxMem
				$info_o.cacheInfoApres:=Cache info.objects.query("type = :1 AND table = :2";"data table";$tableName_t)
				$info_o.table:=Table name($j_l)
				$info_o.duree:=$ms_l
				$info_o.stamp:=Timestamp
				$test_c.push($info_o)
			End if 
			
			$debut_l:=$debut_l+$pas_l
		End while 
	End if 
End for 
UI_progressEnd ($prog_l)
$sortie_t:=System folder(Desktop)+Current method name+Generate UUID+".json"
TEXT TO DOCUMENT($sortie_t;JSON Stringify($test_c;*))
  //SET TEXT TO PASTEBOARD(JSON Stringify($test_c;*))
  //$0:=$test_c