Maison >développement back-end >Golang >Le mystère des enregistrements manquants : débogage d'une transformation JSON vers CSV dans Go

Le mystère des enregistrements manquants : débogage d'une transformation JSON vers CSV dans Go

Barbara Streisand
Barbara Streisandoriginal
2024-11-02 09:31:311022parcourir

The Mystery of Missing Records: Debugging a JSON-to-CSV Transformation in Go

Lors de mon travail de création d'un utilitaire de transformation de données sur l'un de mes projets parallèles, j'avais besoin de convertir un fichier au format JSON au format CSV. J'ai rencontré un problème délicat qui a pris près d'une heure à déboguer avant d'identifier la cause première.

Le processus aurait dû être simple, composé de trois étapes principales :

  1. Ouvrez le fichier JSON
  2. Analyser ce fichier JSON dans une structure spécifique
  3. Écrivez les données dans un fichier CSV Tout d’abord, pour vous donner une idée, le JSON est un tableau de 65 342 éléments.
func JsonToCSV(data *SrcSheet) {
    // Create file name in a format like "email_241030172647.csv" (email_yymmddhhmmss.csv)
    fName := fileName()

    // Create file
    f, err := os.Create(fName)
    if err != nil {
        log.Println("Unable to create file", err)
        return
    }
    defer f.Close() // Closing to release resources
    w := csv.NewWriter(f) // Initializing CSV writer

    // Add header
    header := []string{"email", "provider", "added_on"}
    if err = w.Write(header); err != nil {
        log.Println("Unable to write header", err)
        return
    }

    count := 0
    for domain, elm := range data.Email {
        if err := w.Write(newRecord(domain, elm)); err != nil {
            log.Println("Unable to add new record", domain, err)
            return
        } else {
            count++
        }
    }

    log.Println("Number of records written =", count)
}

func newRecord(email string, e *SrcElements) []string {
    if e == nil {
        return nil
    }

    DBFormat := "2006-01-02 15:04:05.000"
    addedOn := time.Now().UTC().Format(DBFormat)

    r := []string{email, e.Provider, addedOn}
    return r
}

Le code est simple : créez un nouveau fichier avec un format de nom spécifique, différez sa fermeture, initialisez le graveur CSV et commencez à écrire dans le fichier. Super simple, non ?

Les étapes 1 et 2 ont bien fonctionné, je les ai donc omises. Concentrons-nous sur l'étape 3, où quelque chose d'inattendu s'est produit : la sortie CSV ne contenait que 65 032 enregistrements, ce qui signifie qu'il manquait 310 enregistrements.

Pour résoudre le problème, j'ai essayé le code avec seulement 7 éléments JSON au lieu de 65 032. Étonnamment, rien n’a été écrit dans le fichier CSV !

J'ai revérifié les erreurs simples, comme la fermeture manquante d'un fichier, mais tout semblait bien. J'ai ensuite réessayé avec les 65 032 éléments complets, dans l'espoir d'obtenir plus d'indices. C’est alors que j’ai remarqué que non seulement 310 enregistrements manquaient, mais que le dernier enregistrement était également incomplet.

65030 adam@gmail.com, gmail, 2023-03-17 15:04:05.000
65031 jac@hotmail.com, hotmail, 2023-03-17 15:04:05.000
65032 nancy@xyz.com, hotmail, 2023-03-

C'était un progrès : je pouvais maintenant affiner le problème et me concentrer sur w.Write(newRecord(domain, elm)), en particulier la méthode w.Write(...) . J'ai vérifié la documentation et trouvé la raison :

... Les écritures sont mises en mémoire tampon, donc [Writer.Flush] doit éventuellement être appelé pour garantir que l'enregistrement est écrit dans le io.Writer sous-jacent ...

J'avais oublié d'appeler w.Flush(). Cela avait du sens puisque, du point de vue des performances, l'écrivain CSV tamponne l'écriture au lieu d'exécuter des opérations d'E/S à chaque fois que w.Write() est appelé. En mettant les données en mémoire tampon, cela réduit la charge d'E/S et l'appel de w.Flush() à la fin garantit que toutes les données restantes dans la mémoire tampon sont écrites dans le fichier.

Voici le code corrigé :

...
f, err := os.Create(fName)
    if err != nil {
        log.Println("Unable to create file", err)
        return
    }
    defer f.Close()
    w := csv.NewWriter(f)
    defer w.Flush()

    // Add header
    header := []string{"email", "provider", "added_on"}
...

Pour confirmer, j'ai vérifié le code source de bufio.go et j'ai constaté que la taille du tampon par défaut est de 4K. Dans la méthode WriteRune(...), vous verrez qu'elle appelle Flush chaque fois que le tampon atteint sa limite.

C'est tout ! J'espère que vous avez apprécié la lecture. J’ai tendance à apprendre beaucoup de mes erreurs, qu’elles soient les miennes ou celles des autres. Même s’il n’y a pas de solution immédiate, découvrir une mauvaise approche m’aide à éviter des pièges similaires à l’avenir. C'est pourquoi j'ai eu envie de partager cette expérience !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn