Une BSFNUne Business Function est un objet JD Edwards contenant du code (C ou NER) pour exécuter une logique métier spécifique sur le serveur. personnalisée d'allocation d'inventaire traitant 10 000 à 15 000 lignes de commande client devrait s'exécuter en moins d'une minute. Pourtant, dans de nombreux environnements JDE 9.2, cette même exécution prend plus d'une demi-heure à cause d'un anti-pattern classique : l'exécution répétitive d'instructions Select et Fetch Next sur F4101 ou F4102 à l'intérieur d'une boucle. Lorsqu'une Named Event Rule (NER)Langage de programmation propriétaire de JD Edwards permettant de créer de la logique métier sans écrire directement en code C. ou une fonction C déclenche un aller-retour vers la base de données pour chaque itération, la latence réseau entre l'Enterprise Server et le niveau base de données dégrade considérablement les performances.
Corriger ce goulot d'étranglement nécessite plus que l'ajout d'index SQL Server ou Oracle DB. Une véritable optimisation des performances JDE BSFN pour réduire les E/S (Entrées/Sorties)Opérations de lecture et d'écriture de données entre le processeur et le stockage ou le réseau. de table dans les boucles exige des changements architecturaux, spécifiquement le remplacement des E/S ligne par ligne par les API de cache utilisateur JDE (comme jdeCacheInitFonction API permettant d'initialiser un espace de stockage temporaire en mémoire vive (RAM) pour un accès ultra-rapide aux données.) ou le pré-chargement (prefetching) des données en mémoire. En stockant les données de base en mémoire une seule fois, vous éliminez des milliers d'exécutions SQL redondantes et réduisez les temps d'exécution de 90 % à 95 %.
Le coût caché des E/S de table imbriquées dans les boucles
Dans une saisie de commande standard (P42101) ou un UBEUniversal Batch Engine : un programme JD Edwards s'exécutant en arrière-plan pour le traitement de masse ou la génération de rapports. de post-traitement personnalisé, les développeurs imbriquent fréquemment des recherches en base de données dans une boucle principale. Chaque instruction JDB_FetchKeyedUne fonction de l'API JD Edwards utilisée pour récupérer un enregistrement spécifique dans une table de base de données à l'aide d'une clé. ou select exécutée à l'intérieur de cette boucle déclenche un aller-retour réseau individuel et une phase d'analyse (parse) de la base de données. Cette architecture compose la latence de manière exponentielle car le middlewareLogiciel intermédiaire qui assure la communication et la gestion des données entre les applications et la base de données. doit négocier un cycle complet requête-réponse pour chaque ligne, même si le serveur de base de données a mis en cache le plan d'exécution.
Considérons un scénario traitant un lot de 5 000 à 10 000 enregistrements, comme les ajustements d'inventaire quotidiens dans la F4111. Si la fonction exécute plusieurs recherches de table sur une seule ligne — par exemple, la vérification des paramètres de branche d'article dans la F4102, les conversions d'unités de mesure dans la F41002 et les détails d'emplacement dans la F41021 — la boucle exécute des dizaines de milliers d'opérations de base de données distinctes. Ce volume de trafic "bavard" sature rapidement la carte réseau et peut paralyser le kernel JDE CallObjectProcessus serveur JD Edwards responsable de l'exécution des fonctions métier et de la gestion des sessions utilisateur., le laissant dans un état d'attente prolongé pendant que les utilisateurs subissent des blocages de l'application interactive.
Les équipes d'infrastructure tentent souvent de résoudre cette dégradation de performance en reconstruisant les index SQL ou en ajustant les pools de mémoire de la base de données. Bien qu'un indexage approprié occupe les vitesses de lecture brute sur disque, il ne fait rien pour atténuer la latence réseau physique ou le changement de contexte CPU qui se produit entre l'Enterprise Server et le serveur de base de données. Le goulot d'étranglement n'est pas la capacité de la base de données à trouver la ligne ; c'est le volume pur de changements de contexte que le kernel CallObject doit effectuer pour initier, exécuter et fermer des milliers d'appels API JDB discrets.
Avant d'écrire une autre ligne de code C ou de NER, profilez la densité des transactions. Si votre journal d'exécution montre des milliers de requêtes SQL séquentielles pour une seule transaction interactive, vous devez refactoriser la logique pour charger les tables statiques en mémoire une seule fois.
Mesurer la surcharge avec Performance Workbench
Ne refactorisez jamais une ligne de code C ou une Named Event Rule (NER) sur la base d'une simple intuition concernant la table qui ralentit le système. Pour isoler la cause réelle, modifiez votre fichier jde.iniFichier de configuration principal de JD Edwards définissant les paramètres du système, des journaux et des environnements. local, accédez à la section [DEBUG] et réglez Output=FILE avec Keep logs=0 pour capturer un chemin d'exécution propre. L'exécution de l'UBE cible ou de l'application interactive dans cet état génère un fichier jdedebug.log détaillé qui peut facilement dépasser plusieurs gigaoctets, cartographiant chaque appel API de base de données, chaque limite de fonction et chaque instruction SQL exécutée par le kernel CallObject.
L'introduction de ce volumineux fichier journal dans l'utilitaire Oracle Performance WorkbenchOutil d'analyse fourni par Oracle pour diagnostiquer les goulots d'étranglement dans les journaux d'exécution (logs) de JD Edwards. vous permet d'analyser des millions de lignes de données de trace brutes en quelques minutes. L'outil agrège les métriques de performance, isolant spécifiquement la durée exacte des appels API JDB_SelectKeyed, JDB_Fetch et JDB_OpenTable. Il classe ces opérations de base de données par temps d'exécution cumulé, exposant immédiatement quelles tables, comme une table personnalisée F551101, génèrent des allers-retours disproportionnés pendant le traitement.
Une erreur de diagnostic courante consiste à ne rechercher que les instructions SQL lourdes et longues qui prennent plusieurs secondes à s'exécuter. Dans les goulots d'étranglement basés sur des boucles, vous verrez exactement le contraire : une seule instruction SELECT sur F4101 qui s'exécute en moins d'une milliseconde, mais qui a un compteur d'exécution de plusieurs dizaines de milliers. Ce nombre élevé d'exécutions avec une faible durée individuelle est la signature définitive d'un goulot d'étranglement d'E/S basé sur une boucle. L'identification de ce modèle vous indique précisément où remplacer les recherches répétitives en base de données par une stratégie de mise en cache en mémoire avant de promouvoir le code dans l'environnement PY.
Implémentation du JDE Cache pour les données de base statiques
Un UBE de traitement des ventes personnalisé gérant des dizaines de milliers de lignes de commande ne devrait pas exécuter des dizaines de milliers d'instructions SQL SELECT distinctes contre la F4101 pour les mêmes quelques centaines d'articles à rotation rapide. Pour les tables statiques ou semi-statiques comme la F4101 (Item Master) ou la F0010 (Company Constants), charger les données dans un cache utilisateur JDE une fois par transaction élimine entièrement les allers-retours vers la base de données. Cela déplace le goulot d'étranglement de la latence d'E/S de la base de données — qui consomme généralement 2 à 5 millisecondes par requête sur le réseau — vers des recherches à la vitesse de la mémoire mesurées en microsecondes. Dans une architecture standard à 3 niveaux, cette mise en cache locale empêche les délais de sérialisation réseau entre le serveur d'entreprise et le moteur de base de données.
Les fonctions de l'API JDE CacheEnsemble de fonctions permettant de stocker et manipuler des données en mémoire vive pour éviter les accès répétés à la base de données. telles que jdeCacheInit, jdeCacheAdd et jdeCacheFetchPosition stockent des enregistrements structurés directement dans l'espace mémoire du kernel CallObject. Lorsqu'une fonction C initialise un cache, elle définit une structure de clé unique correspondant aux index de la table cible, tels que le numéro court de l'article (ITM) et l'établissement (MCUBusiness Unit ou Établissement : code identifiant une unité organisationnelle ou un site dans JD Edwards.). Les recherches ultérieures contournent entièrement le middleware de la base de données, récupérant la structure de données F4101 pré-chargée directement depuis la RAM locale. Cette architecture évolue de manière linéaire, maintenant des temps de réponse inférieurs à la milliseconde même sous une charge de plusieurs centaines de sessions HTML simultanées.
Les gains de vitesse ne valent rien si vos serveurs d'entreprise plantent en raison d'un épuisement de la mémoire. Les développeurs doivent implémenter un modèle de nettoyage strict en utilisant jdeCacheTerminateFonction API utilisée pour libérer la mémoire occupée par un cache JDE une fois le traitement terminé. dans le bloc de destruction de la BSFN pour éviter les fuites de mémoireProblème survenant lorsqu'un programme ne libère pas la mémoire qu'il n'utilise plus, pouvant entraîner un crash du serveur. dans le middleware JDE. Si un kernel CallObject traite des milliers de transactions sans terminer ses instances de cache, ces segments de mémoire orphelins s'accumuleront. Cela finit par forcer un recyclage administratif du kernel, déconnectant les sessions utilisateur actives pendant les heures de pointe. Associez toujours chaque jdeCacheInit à un appel de terminaison correspondant dans le même thread d'exécution.

Le modèle de Prefetch pour les relations un-à-plusieurs
Traiter des milliers de lignes de détail F4211 en interrogeant la table d'en-tête F4201 à l'intérieur de la boucle est un tueur de performance classique qui se produit dans une grande majorité d'UBE personnalisés de confirmation d'expédition ou de facturation (environ les trois quarts selon notre expérience). Ce modèle de requête N+1Problème de performance où une requête principale déclenche une multitude de requêtes secondaires, saturant les ressources. génère des milliers d'allers-retours individuels vers la base de données, faisant passer les temps d'exécution de quelques secondes à plusieurs minutes. Le pré-chargement (prefetching)Technique consistant à charger des données en mémoire avant qu'elles ne soient demandées pour accélérer le traitement. des enregistrements parents F4201 en masse avant d'entrer dans la boucle de détail élimine entièrement ce trafic de base de données excessif.
Au lieu d'exécuter JDB_FetchKeyed à l'intérieur de la boucle F4211, extrayez d'abord la liste unique des numéros de documents (DOCO), types (DCTO) et sociétés (KCOO). Vous exécutez ensuite une seule instruction SELECT avec une clause IN — ou utilisez une jointure de clé partielle — pour charger ces enregistrements d'en-tête F4201 dans un tableau mémoire trié ou un cache utilisateur JDE léger. Pour un lot typique de 200 à 500 commandes client, cela remplace des milliers d'instructions select individuelles par un seul scan d'index hautement optimisé sur la clé primaire de la F4201.
Une fois que les données d'en-tête résident en mémoire locale, la performance de recherche évolue de manière logarithmique plutôt que linéaire. L'itération à travers le tableau mémoire trié à l'aide d'un algorithme de recherche binaire, tel que la fonction bsearch de la bibliothèque C standard, réduit la latence de recherche de 2 à 5 millisecondes par accès base de données à moins d'une microseconde. Si vous traitez une interface EDI à haut volume avec des dizaines de milliers de lignes, cette optimisation par prefetch réduit les temps d'exécution des UBE de 80 % à 90 %, tombant de plusieurs minutes à moins de deux minutes sans changer un seul index sur la base de données.

Refactorisation de NER en BSFN C pour le contrôle de la mémoire
Les Named Event Rules (NER) constituent un goulot d'étranglement majeur dans le traitement à haut volume car elles n'ont pas d'accès direct aux API JDE Cache. Lorsque les développeurs doivent stocker un état transactionnel temporaire entre les itérations dans une NER, ils sont contraints d'utiliser des solutions de contournement inefficaces comme des tables de travail personnalisées (par exemple, une table F5501UI) ou des vues de base de données répétées. Ce modèle de conception introduit des E/S disqueOpérations de lecture ou d'écriture sur le stockage physique, beaucoup plus lentes que les opérations en mémoire vive. lourdes et des verrous de base de données, transformant ce qui devrait être une recherche en mémoire d'une fraction de milliseconde en un accès physique à la base de données de plusieurs millisecondes pour chaque itération de boucle.
La conversion de ces NER à haute fréquence en fonctions C résout cette limitation en offrant une manipulation native des pointeurs, des définitions de structures personnalisées et une gestion directe de la mémoire. Au lieu de laisser le Toolset générer un code C lourd et non optimisé en arrière-plan, l'écriture en C natif vous permet d'allouer la mémoire dynamiquement via jdeAllocFonction API JD Edwards utilisée pour réserver dynamiquement un bloc de mémoire vive pendant l'exécution d'un programme. et de définir des structures précises qui reflètent votre logique métier. Cette transition réduit généralement l'utilisation du CPU sur le serveur d'entreprise de 40 % à 60 % pour les travaux batch traitant de grands ensembles de données comme la F4211 ou la F4111.
Le véritable avantage architectural d'une BSFN C est sa capacité à maintenir l'état à travers plusieurs appels en utilisant les pointeursVariables contenant l'adresse mémoire d'une autre donnée, permettant une manipulation directe et efficace de la mémoire. lpDs->hUser ou lpVoid. Dans une exécution d'UBE multi-étapes typique, vous pouvez initialiser un cache JDE lors du premier appel, stocker l'adresse mémoire de ce cache dans le pointeur réservé à l'utilisateur, et la récupérer instantanément lors des appels de traitement de détail suivants. Cela élimine la surcharge liée à la recherche du cache global par son nom à chaque invocation, permettant un accès persistant au cache à travers différentes étapes d'exécution sans un seul aller-retour vers la base de données.
Validation des gains de performance dans les environnements PY
Effectuer une validation de performance dans un environnement de développement local (DV) avec une base de données tronquée est une perte de temps. Pour capturer la latence réelle du réseau et de la base de données, vous devez exécuter ces tests de validation dans un environnement de Prototype (PY)Environnement de test JD Edwards utilisé pour valider les développements avec des données et des volumes proches de la production. contenant une base de données restaurée à la taille de la production. Sans la distance physique entre le serveur d'entreprise et le serveur de base de données, et sans tables à l'échelle de la production comme la F0911 ou la F4211, vos succès de cache local masqueront la latence réelle qui se produit lorsque des milliers d'utilisateurs simultanés sollicitent le système.
Pour établir une base de référence propre, construisez un pilote UBE simple et standardisé — tel qu'un rapport personnalisé R5501I6 — conçu pour exécuter la fonction métier cible de manière séquentielle sur un lot de 5 000 à 10 000 enregistrements. Exécutez ce pilote une fois avec l'ancienne configuration BSFN, videz le cache de la base de données JDE via le Server ManagerInterface d'administration Web pour gérer, configurer et surveiller les serveurs et instances JD Edwards. pour éviter que les résultats mis en cache ne faussent l'exécution suivante, puis exécutez le même pilote avec votre code refactorisé. Cette comparaison directe isole l'exécution de la logique métier des temps de rendu des écrans APPL interactifs, vous fournissant des fichiers journaux clairs et authentiques dans la file d'attente d'impression.
Une refactorisation réussie basée sur le cache ou le prefetch doit produire une réduction de 90 % à 95 % du temps d'exécution de la base de données pour être considérée comme achevée. Lors d'une récente optimisation d'une boucle de disponibilité des articles F41021 pendant une mise à niveau vers la 9.2, cette approche spécifique a fait chuter le temps total d'exécution de la BSFN de plus de dix minutes à moins d'une demi-minute pour un bloc de 10 000 lignes. Lorsque vous éliminez des milliers d'instructions SQL SELECT individuelles et les remplacez par des recherches par pointeur mémoire, l'utilisation du CPU de la base de données stagne et la surcharge de transport réseau tombe presque à zéro.
Si votre parc de code personnalisé dépasse plusieurs milliers d'objets, le ralentissement cumulé dû à des E/S de table inefficaces dans les boucles BSFN peut facilement représenter une part importante du temps d'exécution des UBE, environ un tiers à la moitié selon notre expérience.