Pratique de construction de l'outil de test DIFF du service de données basé sur GoldenEye PAAS | Équipe technique JD Cloud

1. Introduction générale

Le service de données GoldenEye PAAS est une série de services de données qui mettent en œuvre le même contrat de service d'indicateurs. Chaque service est divisé en fonction du thème des indicateurs produits. Par exemple, le service de transactions en temps réel fournit la requête d'indicateurs de transactions en temps réel, et le service financier hors ligne fournit l'interrogation d'indicateurs financiers hors ligne. Le service de données GoldenEye PAAS prend en charge les besoins de requête de données de GoldenEye APP, GoldenEye PC et de divers grands écrans internes. Pour que l'entreprise puisse prendre des informations et prendre des décisions correctes en matière de données, il est nécessaire de garantir l'exactitude des données fournies par les services de données basés sur PAAS .

Avec l'itération rapide des exigences métier, les services de données doivent souvent ajuster le calibre, les dimensions et les indicateurs de requête des requêtes. Par conséquent, des méthodes de tests de régression automatisées efficaces sont nécessaires pour garantir la qualité de chaque itération d'exigence. Les tests automatisés des services de données PAAS sont confrontés à certains problèmes :

1. Il existe de nombreux scénarios de requête : le protocole de service d'indicateur prend en charge un grand nombre d'indicateurs et de dimensions de requête, et il est difficile pour les tests manuels de couvrir tous les scénarios de requête en ligne ;
2. Paramètres de requête complexes : les paramètres de requête du protocole de service d'indicateur sont des objets imbriqués complexes, et il est difficile pour l'outil Python DIFF existant d'appeler l'interface JSF du service de données via http ;
3. Il est difficile de comparer les paramètres de retour : les paramètres de retour du protocole du service d'indicateur utilisent des index pour localiser les valeurs correspondant aux noms de champs. Si les index de champ sont incohérents ou si les éléments de l'ensemble de données sont en panne , il est facile de provoquer de fausses alarmes en comparaison.

En réponse à la diversité des scénarios de requêtes et à la particularité du protocole de service d'indicateur, nous avons mis en place un outil DIFF dédié aux services de données GoldenEye PAAS. En enregistrant le trafic en ligne, puis en le restituant dans les environnements de test et en ligne, en comparant les résultats renvoyés, et les problèmes d'efficacité de lecture des demandes et de résultats de comparaison faux positifs ont été spécifiquement optimisés. Cette méthode peut garantir une couverture complète des scénarios de requêtes en ligne, réduire les interventions manuelles dans la construction manuelle de scénarios de requêtes et fournir des capacités de tests de régression automatisés suffisamment efficaces pour permettre l'auto-test de R&D.

Après une période de construction et d'itération, l'outil DIFF de service de données basé sur PAAS de GoldenEye a atteint la couverture des trois services de GoldenEye : le trading en temps réel, le trading hors ligne et la finance hors ligne, avec un nombre mensuel moyen d'exécutions DIFF de plus de 40 fois.

L'article suivant présentera les défis et les expériences pratiques rencontrés dans la construction des outils DIFF de service de données basés sur GoldenEye PAAS.

2. Problèmes et idées

2.1 Difficultés d'appel des protocoles PAAS

Les services GoldenEye PAAS implémentent tous le même protocole de service d'indicateur. Le paramètre de requête est un objet imbriqué complexe et chaque objet de condition de filtre dans la collection de critères utilise l'annotation @JsonSubTypes de Jackson pour implémenter l'analyse polymorphe. Selon le document d'appel http de l'équipe JSF, l'appel d'une interface avec des objets imbriqués complexes nécessite l'ajout de "@type": "chemin du package + nom de classe" à chaque objet imbriqué, et il est recommandé d'utiliser JSON.toJSONString (instance, SerializerFeature.WriteClassName ) génère une chaîne JSON avec des caractéristiques de type, comme le montre la figure ci-dessous. Avant la génération, Jackson doit être utilisé pour désérialiser les paramètres de la requête en objets d'instance. Si vous insistez pour utiliser la méthode http pour appeler ce type d'interface JSF dans un script Python, vous devez identifier vous-même la classe d'implémentation de chaque condition de filtre . Afin de réaliser l'appel pratique de l'interface JSF du service de données PAAS, l'outil DIFF écrit en Java est né.



2.2 Principaux défis

L'outil DIFF écrit en Java peut résoudre le problème de l'appel d'interface de service de données basé sur PAAS, mais dans l'utilisation réelle des scripts DIFF existants, il est confronté aux deux défis suivants :

2.2.1 Problèmes d'efficacité de lecture des demandes

Lecture d'un seul fil

L'appel d'interface et la comparaison des résultats des requêtes enregistrées sont implémentés par un seul thread parcourant la liste des requêtes. Une fois qu'une seule requête a appelé avec succès l'interface des environnements en ligne et de test, les résultats renvoyés sont comparés. Seulement une fois la comparaison terminée. retour à la demande suivante. Mais en fait, il n’y a pas de séquence d’appel entre la lecture de la requête précédente et la requête suivante. Cette méthode d'utilisation d'un seul thread pour la lecture des requêtes réduira considérablement l'efficacité de l'exécution et rendra le temps d'exécution de la comparaison trop long.

Interface d'appel synchrone

De plus, les appels aux interfaces de l'environnement en ligne et de test sont synchronisés. Une fois que l'interface d'un environnement a renvoyé un résultat, l'interface suivante est appelée. De la même manière, il n'y a aucune dépendance entre les appels d'interface des deux environnements. La valeur de retour d'une interface d'environnement n'affectera pas l'appel de l'interface suivante. Il vous suffit de vous assurer que les deux appels d'interface d'environnement ont des valeurs de retour, et puis comparez les résultats.

2.2.2 Le problème des faux positifs dans les résultats de comparaison

Les index sont incohérents

La structure des paramètres de retour du protocole PAAS est la suivante. On peut voir que l'ensemble de données body.data contient le plus d'éléments d'information et constitue la principale cible de comparaison. L'index de données meteData.metaIndex définit les champs spécifiques représentés par chaque valeur dans les sous-éléments de l'ensemble de données. Nom, par exemple, les trois valeurs dans [1234,12345.41,"11068"] représentent respectivement le volume de la commande parent de la transaction, le montant de la transaction et l'identifiant de la marque principale. Si les index des noms de champs de valeur de retour des deux interfaces d'environnement ne sont pas cohérents, mais que les valeurs correspondant aux noms de champs réellement obtenus selon l'index sont les mêmes, dans ce cas, parcourez directement la sous-collection de collecte de données pour comparaison , vous obtiendrez une conclusion différente. Mais en fait, les deux valeurs de retour sont cohérentes, ce qui conduit à des faux positifs dans les résultats de la comparaison, comme le montre la figure ci-dessous.



tableau non ordonné

En plus des index de noms de champs incohérents, qui peuvent conduire à des faux positifs en comparaison, chaque sous-collection de données de l'ensemble de données peut être dans le désordre. Dans les données des paramètres de retour de l'exemple, la première sous-collection contient deux indicateurs de la marque principale "11068", et la deuxième sous-collection contient deux indicateurs de la marque principale "231966". Si les données des paramètres de retour d'un autre environnement interface, la marque principale d'une sous-collection est "231966", et la marque principale de la deuxième sous-collection est "11068". Dans ce cas, le parcours direct et la comparaison entraîneront des différences dans les résultats de la comparaison, mais en fait c'est aussi un faux positif.



2.3. Idées de solutions

2.3.1 Concurrence pendant les requêtes de traversée et asynchrone pendant la lecture du trafic pour résoudre le problème de l'efficacité de la lecture des requêtes

De l'analyse du défi principal, on peut voir que la consommation de temps d'une exécution DIFF = le nombre de requêtes qui doivent être lues * (la consommation de temps des appels d'interface dans les deux environnements + la consommation de temps de comparaison) . La formule de fractionnement fastidieuse montre que l'optimisation de l'efficacité de l'exécution DIFF peut être obtenue dans deux directions : demande de lecture parallèle et appel d'interface asynchrone.

1. Demande de lecture parallèle : lors du traitement de la demande, soumettez la tâche de lecture et de comparaison d'une seule demande au pool de threads. Une fois les tâches de lecture et de comparaison de toutes les demandes terminées, les données du résultat de la comparaison sont résumées et un rapport DIFF est généré. ;
2. Appel asynchrone d'interface : utilisez CompletableFuture pour appeler de manière asynchrone les interfaces des deux environnements pour obtenir cf1 et cf2, puis utilisez la méthode cf1.thenCombine(cf2) pour combiner les résultats d'exécution des deux tâches asynchrones. Une fois les deux tâches asynchrones terminées, exécuté, puis passez à l'étape suivante.

2.3.2 L'unification de l'index de champ et le tri secondaire sont mis en œuvre pour résoudre le problème des faux positifs dans les résultats de comparaison

Pour la structure des paramètres de retour du protocole PAAS, afin d'obtenir zéro faux positif dans les résultats de comparaison, il est nécessaire d'assurer l'unification des index de champ de valeur de retour des deux environnements et l'ordre des sous-collections au sein de l'ensemble de données. , de sorte que lors de la comparaison des résultats de la valeur de retour, il n'y aura aucune anomalie de contraste.

L'implémentation unifiée de l'index de champ est relativement simple. Sur la base du métaIndex d'index de champ de la valeur de retour d'une interface d'environnement, réorganisez la position de chaque valeur dans la sous-collection de données dans la valeur de retour d'une autre interface d'environnement pour garantir que chaque valeur de la sous-collection de données est parcourue. , les noms de champs correspondants sont cohérents.

Le tri secondaire vise à résoudre le problème d'ordre entre les sous-ensembles de données. La raison pour laquelle l'accent est mis sur "secondaire" est que le sous-ensemble peut contenir plusieurs valeurs. Si une seule valeur est triée, le sous-ensemble ne peut pas être complètement garanti. Par conséquent, il est nécessaire de mettre en œuvre un trieur secondaire pour sélectionner plusieurs valeurs dans le sous-ensemble comme base de tri , afin de stabiliser l'ordre entre les sous-ensembles et d'assurer la fiabilité des résultats de la comparaison.

3. Structure globale



4. Points de conception fondamentaux

4.1 Demander l'optimisation de l'efficacité de la lecture

4.1.1 Demander la déduplication avant la lecture

Avant de créer l'outil DIFF du service de données, l'un des objectifs était de « donner du sens à chaque demande de lecture ». Pour les services de requête de données en lecture seule, il y aura un grand nombre de requêtes répétées chaque jour. Si les requêtes ne sont pas dédupliquées, alors une grande partie des requêtes de lecture seront répétées. De telles requêtes ne constituent pas seulement un gaspillage de ressources pour la lecture. et DIFF , interférant avec le résumé des résultats finaux de la comparaison, n'a pas encore de sens. Par conséquent, un traitement de déduplication avant de demander la lecture est nécessaire.

Initialement, la méthode de déduplication des paramètres de requête adoptée par l'outil DIFF est la méthode HashSet. Avant d'appeler l'interface pour lire le trafic, la déduplication de la demande est enregistrée dans un HashSet. Une fois la déduplication terminée, le HashSet est parcouru pour la lecture du trafic et Exécution DIFF. Bien que cette méthode puisse supprimer efficacement les doublons, en utilisation réelle, si le nombre de requêtes nécessitant une différence est trop important ou si les paramètres de requête sont trop volumineux (par exemple, interroger les indicateurs financiers de 2 000 SKU spécifiés), alors l'efficacité de la déduplication HashSet est relativement faible, et le parcours de déduplication de 50 000 requêtes volumineuses prend plus de 10 minutes et occupe beaucoup de mémoire. Si la mémoire du conteneur de pipeline qui exécute DIFF est petite, l'exécution de DIFF échouera directement.

Afin d'améliorer l'efficacité de la déduplication des requêtes, la méthode du filtre Bloom est utilisée pour réaliser la déduplication. La mémoire occupée est plus petite que la méthode HashSet, et l'efficacité pour déterminer s'il y a des doublons est également relativement élevée. Elle convient aux opérations de déduplication de grandes quantités de données .

for (int i = 0; i < maxLogSize; i++) {
    String reqParam = fileAccess.readLine();
    if (bloomFilter.mightContain(reqParam)){
        //请求重复,跳过此请求
        countDownLatch.countDown();
        continue;
    }else {
        bloomFilter.put(reqParam);
    }
    //请求不重复,执行接口调用和DIFF    
}

4.1.2 Concurrence lors du parcours des requêtes

Lors du traitement des requêtes,utilisez la méthodeexecute() pour soumettre les tâches de lecture des requêtes et d'exécution DIFF au pool de threads.Chaque requête générera une tâche indépendante et les tâches seront exécutées en parallèle, améliorant ainsi l'efficacité d'exécution de l'ensemble du processus DIFF. Étant donné que DIFF doit effectuer le résumé et l'affichage des différences de comparaison après chaque demande de lecture et de comparaison des résultats, le thread principal doit être bloqué et attendre la fin de tous les sous-threads de la tâche de comparaison de lecture avant de démarrer le thread principal pour une analyse récapitulative. de résultats de différence.

Afin de répondre aux exigences de synchronisation des appels entre le thread principal et les sous-threads , le compteur CountDownLatch est utilisé. Le nombre maximum de requêtes n est défini lors de l'initialisation. Chaque fois qu'un sous-thread se termine, le compteur est décrémenté de un. Lorsque le compteur devient 0, le thread principal qui attendait se réveille et résume les différences de résultats. .

Cela implique à nouveau le problème de la politique de rejet du pool de threads. Si vous utilisez la politique AbortPolicy par défaut du pool de threads, mais n'interceptez pas les exceptions et ne décrémentez pas le compteur de un pendant la gestion des exceptions, alors lorsque le pool de threads ne peut pas accepter de nouvelles tâches, le compteur CountDownLatch le fera. ne soit jamais 0, le thread principal est toujours bloqué et le processus DIFF ne se terminera jamais. Afin de permettre à DIFF de s'exécuter en douceur et de produire des résultats de comparaison dans des circonstances anormales, et de garantir que chaque demande peut être lue et comparée, CallerRunsPolicy est utilisé ici pour renvoyer les tâches rejetées à l'appelant pour exécution.

//初始化计数器
CountDownLatch countDownLatch = new CountDownLatch(maxLogSize);
log.info("开始读取日志");
for (int i = 0; i < maxLogSize; i++) {
    String reqParam = asciiFileAccess.readLine();
    if (bloomFilter.mightContain(reqParam)){
        //有重复请求,跳出本次循环,计数器减一    
        countDownLatch.countDown();
        continue;
    }else {
        bloomFilter.put(reqParam);
    }
    //向线程池提交回放和对比任务
    threadPoolTaskExecutor.execute(() -> {
        try {
            //执行接口调用和返回结果对比
            }
        } catch (Exception e) {
        } finally {
            //子线程一次执行完成,计数器减一
            countDownLatch.countDown();
        }
    });
}
//阻塞主线程
countDownLatch.await();
//等待计数器为0后,开始汇总对比结果

4.1.3 Asynchrone lors de la demande de lecture

JSF fournit une méthode d'appel asynchrone d'interface, renvoyant un objet CompletableFuture<T>. Vous pouvez facilement utiliser la méthode thenCombine pour combiner l'interface de l'environnement de test et la valeur de retour de l'appel de l'interface de l'environnement de test, effectuer une comparaison des valeurs de retour et renvoyer le résultat de la comparaison.

//测试环境接口异步请求
CompletableFuture<UResData> futureTest = RpcContext.getContext().asyncCall(
    () -> geTradeDataServiceTest.fetchBizData(param)
);
//线上环境接口异步请求
CompletableFuture<UResData> future = RpcContext.getContext().asyncCall(
    () -> geTradeDataServiceOnline.fetchBizData(param)
);
//使用thenCombine对futureTest和future执行结果合并处理
CompletableFuture<ResCompareData> resultFuture = futureTest.thenCombine(future, (res1, res2) -> {
//执行对比,返回对比结果
return resCompareData;
});
//获取对比结果的值
return resultFuture.join();

4.2 Optimisation du taux de fausses alarmes

4.2.1 Unification de l'index de champs

L'essentiel de l'unification des index de champ est de trouver la relation de mappage entre le même nom de champ index entre deux valeurs de retour.Par exemple, l'index index1 du nom de champ a dans la valeur de retour A correspond à l'index index2 du nom de champ a dans la valeur de retour B . Une fois toutes les relations de mappage trouvées, l’ordre des champs dans le tableau de valeurs de retour B est réorganisé.

List<List<Object>> tmp = new ArrayList<>();
int elementNum = metaIndex1.size();
HashMap<Integer, Integer> indexToIndex = new HashMap<>(elementNum);
//获取索引映射关系
for (int i = 0; i < elementNum; i++) {
    String indicator = getIndicatorFromIndex(i, metaIndex2);
    Integer index = metaIndex1.get(indicator);
    indexToIndex.put(i, index);
}
//根据映射关系重新排放字段,生成新的数据集合
for (List<Object> dataElement : data2) {
    List<Object> tmpList = new ArrayList<>();
    Object[] objects = new Object[elementNum];
    for (int i = 0; i < dataElement.size(); i++) {
        objects[indexToIndex.get(i)] = dataElement.get(i);
    }
    Collections.addAll(tmpList, objects);
    tmp.add(tmpList);
}

4.2.2 Tri secondaire des ensembles de données

Les données de collecte de données contiennent plusieurs sous-collections, et chaque sous-collection est une combinaison de valeurs d'attribut et d'indicateurs. Afin de réaliser le tri entre les sous-collections, il est nécessaire de trouver les index de tous les champs d'attributs et d'implémenter un trieur secondaire qui utilise les valeurs de tous les champs d'attributs comme critère de tri.

De manière générale, le type de données des champs d'attribut est de type String, mais il existe des cas particuliers. Par exemple, si sku_id est utilisé comme valeur d'attribut, alors le type de données renvoyé par sku_id est de type Long. Afin de réaliser l'universalité et la fiabilité du trieur secondaire, utilisez d'abord tous les champs de type String pour trier, puis utilisez les champs de type Long pour trier. S'il n'y a pas de champ de type String, alors tous les champs de type Long sont triés.

List<Integer> strIndexList = new ArrayList<>();
List<Integer> longIndexList = new ArrayList<>();
// 保存所有String和Long类型字段的索引
for (int i = 0; i < size; i++) {
    if (data.get(0).get(i) instanceof String) {
        strIndexList.add(i);
    }
    if (data.get(0).get(i) instanceof Long || data.get(0).get(i) instanceof Integer) {
        longIndexList.add(i);
    }
}
//首选依据String类型字段排序
if (!strIndexList.isEmpty()) {
    //初始化排序器
    Comparator<List<Object>> comparing = Comparator.comparing(o -> ((String) o.get(strIndexList.get(0))));
    for (int i = 1; i < strIndexList.size(); i++) {
        int finalI = i;
        //遍历剩余String字段,实现任意长度次级排序器
        comparing = comparing.thenComparing(n -> ((String) n.get(strIndexList.get(finalI))));
    }
    for (int i = 0; i < longIndexList.size(); i++) {
        int finalI = i;
        comparing = comparing.thenComparingLong(n -> Long.parseLong(n.get(longIndexList.get(finalI)).toString()));
    }
    sortedList = data.stream().sorted(comparing).collect(Collectors.toList());
}

4.3 Effet DIFF du service de données PAAS

Après avoir optimisé l'efficacité de la lecture des requêtes et le taux de fausses alarmes, la consommation de temps et les résultats de comparaison d'une exécution DIFF du service de données peuvent répondre aux besoins des autotests et de la régression R&D existants avant la mise en ligne. La figure ci-dessous montre un rapport DIFF du service de trading en temps réel GoldenEye. Sous la configuration de quatre threads de travail, après la déduplication de 40 000 requêtes, la lecture de la requête a pris 570 secondes. Les résultats de la comparaison peuvent également montrer clairement les différences de données. Afin de faciliter l'enquête sur les différences de données, lors de l'affichage des résultats de la comparaison des différences, les noms de champs sont épissés avant les valeurs pour éviter de rechercher manuellement les noms de champs correspondant aux valeurs de différence basées sur l'index.



5. Mise en œuvre

5.1 Pipeline d'accès aux tests des services de données

Le pipeline fournit des atomes de code de téléchargement, des atomes de compilation et des atomes de script personnalisés Shell. Ces trois atomes peuvent déclencher l'exécution de tâches DIFF. Grâce à cette capacité du pipeline, la capacité des outils DIFF a été introduite dans le pipeline d'accès aux tests de GoldenEye GoldenEye négociant des services de données en temps réel et hors ligne. Le pipeline d'accès aux tests comprend la compilation et le déploiement JDOS des codes de branche de test, la lecture du trafic et la comparaison des résultats d'exécution, ainsi que les statistiques de couverture des tests d'interface pendant le processus DIFF. Grâce à la lecture du trafic en ligne pour les tests DIFF, la couverture en ligne du service de transaction hors ligne et du code de service en temps réel a atteint respectivement 51 % et 65 % .

Pipeline d'accès aux tests de service hors ligne de transaction

Pipeline d'accès aux tests de services en temps réel de transaction

5.2 Auto-test de R&D des services pour améliorer l'efficacité

En plus du rôle de l'outil DIFF dans le pipeline d'admission des tests, nous avons également mis en place un pipeline qui peut déclencher manuellement le DIFF pour permettre les autotests de R&D et améliorer l'efficacité. À l'heure actuelle, le pipeline DIFF a été mis en œuvre dans plusieurs services de données de GoldenEye PAAS, notamment le service de transactions hors ligne, le service de transactions en temps réel et le service financier hors ligne. La figure ci-dessous montre les délais d'exécution mensuels du pipeline DIFF de ces trois services de données. Les délais d'exécution cumulés en septembre sont : 66 fois DIFF .



*Source de données : statistiques sur les tendances d'exécution du pipeline

À l'heure actuelle, l'outil DIFF a déjà la capacité de personnaliser le seuil du taux de différence, de filtrer et d'exclure des mots-clés, et de personnaliser les conditions de filtrage et les indicateurs de demande pour les demandes PAAS des services d'indicateurs. Cette capacité de personnalisation des demandes de services d'indicateurs basées sur PAAS joue un certain rôle dans l'itération quotidienne de la demande et la transformation technique.

Par exemple, le moteur de requête du service de trading en temps réel GoldenEye est passé d'Elasticsearch à Doris. Cette transformation a impliqué de nombreux travaux de tests de régression DIFF. Au cours de ce processus, R&D a constaté qu'il existe certaines différences dans les méthodes de calcul d'Elasticsearch et de Doris pour les indicateurs de déduplication (telles que les quantités de sous-éléments). Par conséquent, le taux de différence des indicateurs de déduplication interrogés à l'aide des deux moteurs sera supérieur à celui des autres indicateurs de non-déduplication (tels que le montant), cela va interférer avec l'affichage des résultats finaux de la comparaison et n'est pas propice à la découverte de requêtes vraiment différentes. Afin de résoudre ce problème, nous offrons la possibilité de personnaliser le service d'indicateursIndicateurs de demande basés sur PAAS.Vous pouvez définir la demande de lecture pour interroger uniquement les indicateurs de non-déduplication, évitant ainsi l'interférence des indicateurs de déduplication et favorisant la progression des tests de régression. .

6. Résumé

Cet article présente les deux problèmes majeurs d'efficacité DIFF et de fiabilité des résultats de comparaison rencontrés par l'équipe de qualité des données GoldenEye lors de la construction de l'outil DIFF du service de données. Il développe également les solutions et l'expérience pratique dans la mise en œuvre des deux types de problèmes ci-dessus et les problèmes correspondants. défis respectivement.

6.1 Avantages des outils DIFF personnalisés

Comparé à la plateforme professionnelle interne d'enregistrement et de lecture R2 de JD.com, notre outil DIFF est bien insuffisant ou absent en termes d'enregistrement du trafic, de suivi des liens, de polyvalence et d'autres fonctionnalités. Cependant, du point de vue de la personnalisation DIFF du service de données PAAS, l'optimisation réalisée par notre outil DIFF dans le processus de personnalisation et de comparaison des demandes n'est actuellement pas prise en charge par cette plateforme universelle.

6.2 Perspectives d'avenir

À l'heure actuelle, l'outil DIFF est utilisé pour les tests de régression automatisés.Le taux de couverture des lignes de code des tests d'interface du service de trading hors ligne GoldenEye et du service en temps réel est d'environ 50%, ce qui montre que les scénarios de requêtes en ligne quotidiennes ne peuvent pas couvrir toutes les branches de code. Afin d'améliorer la couverture des lignes de code du test DIFF, nous envisagerons d'analyser le rapport de couverture ultérieurement pour connaître les scénarios non couverts par la requête en ligne et former un ensemble fixe de cas d'utilisation de requêtes. prend en charge à la fois le trafic en ligne et les cas d'utilisation de requêtes fixes. Conçu pour améliorer la couverture des lignes des tests DIFF.

Auteur : JD Retail Lei Shiqi

Source : Communauté de développeurs JD Cloud Veuillez indiquer la source lors de la réimpression

Lei Jun : La version officielle du nouveau système d'exploitation de Xiaomi, ThePaper OS, a été packagée. La fenêtre pop-up sur la page de loterie de Gome App insulte son fondateur. Ubuntu 23.10 est officiellement publié. Autant profiter de vendredi pour mettre à jour ! Épisode de sortie d'Ubuntu 23.10 : L'image ISO a été "rappelée" de toute urgence en raison de la présence de discours de haine. Un doctorant de 23 ans a corrigé le "bug fantôme" de 22 ans dans Firefox. Le bureau à distance RustDesk 1.2.3 a été publié, Wayland amélioré pour prendre en charge la version TiDB 7.4 : compatible officiel avec MySQL 8.0. Après avoir débranché le récepteur USB Logitech, le noyau Linux s'est écrasé. Le maître a utilisé Scratch pour frotter le simulateur RISC-V et a exécuté avec succès le noyau Linux. JetBrains a lancé Writerside, un outil pour la création de documents techniques.
{{o.name}}
{{m.nom}}

Je suppose que tu aimes

Origine my.oschina.net/u/4090830/blog/10120050
conseillé
Classement