Executando verificação de segurança...
2

Cuidado com o escopo de suas variáveis: gh-cleaner e seu bug inesperado

Motivação

Para quem viu meu Post anterior sabe que estou trabalhando com um projeto de deletador de repositórios github com inteligencia artificial, mais especificamente o classificador chamado naive bayes.

Este é um exercicio de programação citado pelo Akita em Entendendo o Dilema das Redes.

E para fazer isso, se você estiver acompanhando meu repositório no github pode ver que eu estava separando em um local escondido algumas pastas chamada "bayes_theorem/savedRepo" e "bayes_theorem/deletedRepo" contendo um json com todas as estruturas de github.Repository.

Esse é um fato importante já que eu quero descobrir qual seria o principal caracter de um repositório que me fez deleta - lo e sortear a lista de repositórios para probabilisticamente descobrir qual é o repositório que eu tenho a maior chance de deletar conforme os anteriores.

O infrator

Mas no momento em que escrevi essa função chamada ReadJson, eu vi um comportamento estranho, o repositório que deveria ser deletedRepo apareceu savedRepo. E eu cacei, fucei e tudo mais para encontrar o problema.

package fileutils

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/google/go-github/v61/github"
)

var Repos []*github.Repository

func ReadJson(path string) []*github.Repository {
	exists := FileExists(path)

	if exists {
		arquivos, err := os.ReadDir(path)
		if err != nil {
			fmt.Printf("Error in opening the read dir %s: %v\n", path, err)
			return nil
		}

		for _, arquivo := range arquivos {
			var repo *github.Repository
			if !arquivo.IsDir() && strings.HasSuffix(arquivo.Name(), ".json") {
				data, err := os.ReadFile(path + "/" + arquivo.Name())
				if err != nil {
					fmt.Printf("Error in read the file %s: %v\n", path, err)
					return nil
				}

				err = json.Unmarshal(data, &repo)
				if err != nil {
					fmt.Printf("Error in unmarshaling the json: %v", err)
					return nil
				}

				Repos = append(Repos, repo)
			}
		}

		return Repos
	}

	fmt.Printf("Error path %s not founded\n", path)
	return nil
}

Poizé, o problema está justamente na variável global Repos que eu havia criado.

Isso aconteceu por eu chamo duas vezes a ReadJson no meu projeto, uma para pegar os savedRepo e outra para pegar os deletedRepo e contendo uma variável global a memória não é limpa ao encerrar a função, fazendo com que esse comportamento inesperado a aparecesse.

A solução

Então para resolver isso a solução mais obvia foi mover a variavel global para uma variavel local e devolver uma copia do valor

func ReadJson(path string) []github.Repository {
	var Repos []github.Repository
	exists := FileExists(path)

	if exists {
		arquivos, err := os.ReadDir(path)
		if err != nil {
			fmt.Printf("Error in opening the read dir %s: %v\n", path, err)
			return nil
		}

		for _, arquivo := range arquivos {
			var repo github.Repository
			if !arquivo.IsDir() && strings.HasSuffix(arquivo.Name(), ".json") {
				data, err := os.ReadFile(path + "/" + arquivo.Name())
				if err != nil {
					fmt.Printf("Error in read the file %s: %v\n", path, err)
					return nil
				}

				err = json.Unmarshal(data, &repo)
				if err != nil {
					fmt.Printf("Error in unmarshaling the json: %v", err)
					return nil
				}

				Repos = append(Repos, repo)
			}
		}

		return Repos
	}

Isso foi o suficiente para eu conseguir corrigir minha função. E quero deixar o adendo que esse tipo de erro pode ocorrer muito facilmente se você não prestar atenção no escopo das variaveis do seu código principalmente ao trabalhar com copia / referencia ou ponteiros já que na pressa você pode não ver a bomba chegando no colo.

Felizmente consegui encontrar a batata quente a tempo. Espero que essa dica possa ajudar nos seus projetos.