레이블이 golang인 게시물을 표시합니다. 모든 게시물 표시
레이블이 golang인 게시물을 표시합니다. 모든 게시물 표시

go download error in etcd build

# etcd 소스 빌드
wget https://github.com/etcd-io/etcd/archive/refs/tags/v3.5.18.tar.gz
tar zxvf v3.5.18.tar.gz
cd etcd-3.5.18
make

# make > build.sh > script/test_lib.sh > determine_go_version 에서
# .go-version 파일에서 사용할 go version 을 찾고 다운로드 한다.
# 여기서 exit code 1 발생으로 빌드가 중지된다.
GO_BUILD_FLAGS="-v" ./build.sh
go: downloading go1.22.11 (darwin/arm64)
go: download go1.22.11: golang.org/toolchain@v0.0.1-go1.22.11.darwin-arm64: verifying module: checksum database disabled by GOSUMDB=off

# 수동으로 해당 go 버전을 설치하면 된다.
# /Users/ysoftman/workspace/gopath/bin/go1.22.11 가 생긴다.
go install golang.org/dl/go1.22.11@latest

# ~/sdk/go1.22.11/ 에 go 소스 빌드 파일이 생긴다.
go1.22.11 download

# 또는 다음 환경변수 설정으로 시스템 go 버전을 사용해 빌드할 수 있다.
FORCE_HOST_GO=ON make

go test parallel fail

# go test 에서 테스트 함수 하나가 가끔 실패한다.
# 테스트 함수에 t.Parallel() 를 추가 후 발생 빈도가 많아졌다.
# 참고로 gotest 를 사용하면 테스트 결과가 컬러로 표시된다.
go install github.com/rakyll/gotest@latest

# 에러가 발생하는 테스트 함수는 항상 같다.
# 다음과 같이 count 옵션으로 cache 를 사용하지 않고 테스트 함수가 많이 테스트 되도록 하면 100% 발생한다.
gotest -run TestMyQuery -count 100 ./...

# 서비스 로직은 Object-Relational Mapping(ORM) 으로 gorm 을 사용해 mysql 쿼리한다.
# 서비스 로직을 테스트 하기 위한 함수는 다음과 같이 sqlmock 을 사용한다.
func TestMyQuery(t *testing.T) {
t.Parallel()
mockDB, mock, err := sqlmock.New()
if err != nil {
log.Fatal(err)
}
gormDB, _ := gorm.Open(mysql.New(mysql.Config{
Conn:                      mockDB,
SkipInitializeWithVersion: true,
}), &gorm.Config{})
... 생략 ...
        # MyQuery() 에서 수행할 조회, 업데이트등의 쿼리 명시
        # MyQuery() go 루틴에서 처리하는 쿼리들
mock.ExpectBegin()
mock.ExpectQuery("^SELECT \\* FROM `my_table` WHERE `key`=(.+) LIMIT (.+)$").WillReturnRows(AddMyTableData())
mock.ExpectExec("^UPDATE `my_table` SET (.+)$").WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()

        # MyQuery() 에서 처리하는 쿼리들
mock.ExpectQuery("^SELECT \\* FROM `my_table` WHERE `key`=(.+) LIMIT (.+)$").WillReturnRows(resMyTableRows)
mock.ExpectQuery("^SELECT \\* FROM `my_history` WHERE `my_history`.`id` = (.+)$").WillReturnRows(resMyHistoryRows)
... 생략 ...
        # 테스트 대상 호출
        MyQuery()

# 테스트 실패시 MyQuery() 에서 error 를 출력해 보면 다음과 같이 테스트 함수에 명시한 expect 쿼리 순서가 맞지 않는듯 하다.
call to Query 'SELECT * FROM `my_table` WHERE `key`=? LIMIT ?' with args [{Name: Ordinal:1 Value:ysoftman_test} {Name: Ordinal:2 Value:1}], was not expected, next expectation is: ExpectedBegin => expecting database transaction Begin

# 원인은 MyQuery() 내에서 go 루틴으로 조회,업데이트하는 쿼리가 동작하고 있었고
# 다음 함수로 expect 순서 매칭을 비활성화하면 100번 반복 테스트해도 성공한다.
mock.MatchExpectationsInOrder(false)

gopls modernize

# gopls 0.18 부터 modernize 기능 추가됐다.
# modernize 분석은 최신 go 기능을 사용해 간단하고 명확한 코드로 제안(변경)한다.
# modernize 항목들에 대한 설명은 다음 문서에 명시돼 있다.

# nvim(lazyvim)에서 사용하기
# gopls 0.18 이상 최신 버전을 다시 설치
:MasonInstall gopls

# lsp 플러그인에서 analyses > modernize 사용 여부를 설정(디폴트: true)
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        gopls = {
          settings = {
            gopls = {
              analyses = {
                modernize = true,
... 생략

# nvim 에서 사용시 해당 코드라인에서 -> code -> code action 으로 modernize 적용

#####

# cli 에서 사용하기
# gopls 커맨드 설치
go install golang.org/x/tools/gopls@latest

# 체크 리스트 확인
gopls check $(fd --type file .go)

# 소스 파일 전체에 대해 modernize 방식으로 일괄 변경
go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -test -fix ./...

golang staticcheck

# nvim+golang 개발환경에서 다음 2개의 툴을 사용하고 있었는데,
# golangc-lint : lint
# gopls : hint, staticcheck, hints...
# diagnostics 로 나오는 staticcheck 를 모두 모아 보고 싶어 staticcheck 커맨드를 별도 설치해서 돌려봤다.
# 설치
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck -checks inherit,-ST1005 ./...

# QF(QuickFix)xxx 결과는 보이지 않고, 실제 체크리스트에서 QF 는 없다.
staticcheck -list-checks

# 알고보니 QF 는 nvim,vscode 등의 IDE 환경에서 gopls 로 staticcheck 를 사용시에만 IDE 로 보인다고 한다.
# 참고로 nvim(lazyvim) 에 gopls 설정

# 테스트
# staticcheck 커맨드에선 QF 결과가 없다.
staticcheck ./...
ysoftman.go:7:5: don't use Yoda conditions (ST1017)

# nvim(lazyvim) 에서 <leader> -> x(diagnostics/quickfix) -> x(diagnostics) 보면 QF도 같이 보인다.

# 문제가 있는 라인으로 커서 이동후 <leader> -> c(code) -> a(code action) 으로 수정할 수 있다.
# yoda conditions 수정하기 (https://staticcheck.dev/docs/checks#ST1017)
# golang 에서는 if 내에서 = 로 할당이 안되기 때문에 c++ 처럼 값을 왼쪽에 명시해 할당 실수를 방지하는 스타일로 짜지 않아도 된다. golang 에서도 빌드는 문제 없지만 이런식은 자연스럽지 않다고 요렇게 st 로 정해두고 있다.
# 참고로 스타워즈 yoda 가 어순을 바꿔 말하는데서 유래됐다고 한다.

# 공통 변수에 대해 if / else if 문을 switch 로 수정하기  (https://staticcheck.dev/docs/checks#QF1003)
# switch 로 바꾸면 가독성이 더 좋아 보인다.

LMT KST in golang

golang 에서 한국 로컬 시간 출력시 시대 별로 표시가 달라진다.
기간에 따라
LMT(Local Mean Time)
JST(Japan Standard Time)
KDT(Korea Daylight Time, 한국 써머 타임)
KST(Korea Standard Time)
로 시간도 +08:30 ~ +10:100 까지 다양했다.

시대별 테스트 결과
-> 0100-12-31 09:28:52 +0827 LMT
-> 1908-03-31 08:27:53 +0827 LMT
-> 1908-04-01 08:30:00 +0830 KST
-> 1911-12-31 08:30:00 +0830 KST
-> 1912-01-01 09:00:00 +0900 JST
-> 1945-09-07 09:00:00 +0900 JST
-> 1945-09-08 09:00:00 +0900 KST
-> 1948-05-31 09:00:00 +0900 KST
-> 1948-06-01 10:00:00 +1000 KDT
-> 1960-09-17 09:30:00 +0930 KDT
-> 1960-09-18 08:30:00 +0830 KST
-> 1961-08-09 08:30:00 +0830 KST
-> 1961-08-10 09:00:00 +0900 KST
-> 1987-05-09 09:00:00 +0900 KST
-> 1987-05-10 10:00:00 +1000 KDT
-> 1988-10-08 10:00:00 +1000 KDT
-> 1988-10-09 09:00:00 +0900 KST

golang 에서 time.LoadLocation("Asia/Seoul") 로 존파일을 참고하게 되는데
zdump(timezone dumper)로 timezone 파일을 보면 타임존내용에 시간대별로 구분이 된다.
zdump -v /usr/share/zoneinfo/Asia/Seoul

gofumpt

neovim(nvim) gopls 설정을 업데이트한 후 .go 파일을 저장하면
var 로 선언된 변수가 := 로 간단하게 변경된다.

찾아보니 gopls 설정에 gofumpt(https://github.com/mvdan/gofumpt) 활성화되어 있었다.

gofumpt 는 gofmt, goimports 기능을 포함하면서 더 엄격한 포맷팅 규칙을 적용하는 확장된 포맷팅 방식이다.
그래서 gofumpt 포맷팅된 코드는 gofmt, goimports 검사를 통과한다고 한다.
요런 규칙들이 있다.
- 복합 리터럴에서 선행 또는 후행 빈 줄 제거
- 할당 연산자 다음의 불필요한 빈 줄 제거
- 인터페이스에서 불필요한 빈 줄 제거
- 가독성을 위해 함수에서 빈 줄 대신 ) { 라인 사용

# 바이너리 설치
go install mvdan.cc/gofumpt@latest

# 이전의 .go 파일들에 대해 포맷팅을 해봤는데 개인적으로 깔끔해져서 가독성에 더 좋은것 같다.
gofumpt -w -l $(fd --type file .go)

# 참고, gopls 체크사항 확인
gopls check $(fd --type file .go)

argocd go module invalid version error

# go.mod 에 argo-cd 패키지가 됐는데 다음과 같이 -m(모든 종속성 패키지 출력) 로 모듈 리스트 출력시 에러가 발생한다.
# (재현을 위해 sudo rm -rf ~/workspace/gopath/pkg/mod/k8s.io/)

# 다음 가이드로 go.mod 에 replace 추가해야 한다.

golang pthread_create failed

# github action 과 비슷한 사내 환경에서 golang 이미지 기반의 빌드로 docker build 를 수행하면 다음과 같은 에러가 발생한다.
RUN go install github.com/swaggo/swag/cmd/swag@latest && swag init
go: downloading golang.org/x/sys v0.18.0
runtime/cgo: pthread_create failed: Operation not permitted
SIGABRT: abort

# 이 현상으로 이미 이슈가 등록되어 있지만 아직 해결되진 않았다.

근본적인 원인은 libseccomp 라는 Debian Debian Bookworm 패키지/라이브러리에 사용되는 새로운 syscall 이 차단되었기 때문이라고 한다. 

golang:1.23.3 (태그 버전에 suffix 가 없으면 현재 debian bookworm 버전임)
이미지를 사용할때 발생한 문제로
위 글중에 bullseye 를 사용하면 된다고 하는 커멘트가 있어 
golang:1.23.3-bullseye 로 변경해서 시도하니 된다.

# 참고로 로컬에 이미지 다운 받아 버전 확인해보면 debian 버전이 다르다.
docker run -it golang:1.23.3

docker run -it golang:1.23.3-bullseye

# 참고 golang 이미지

gup

go install 로 $GOPATH/bin 설치한 툴을 업데이트는 요렇게 한다.
ex) go install github.com/vektra/mockery/v2@v2.49.0

$GOPATH/bin 에 많은 툴들을 한번에 업데이트가 되면 좋을 텐데, 현재는 공식적으론 안되고 각각 위와 같이 업데이트를 해줘야 된다.
그래서 찾아보니 reddit 형들이 요런 툴을 알려주네~

# gup 설치
go install github.com/nao1215/gup@latest

# go install 로 설치된 패키지 모두 업데이트
gup update

참고로 이번에 brew 와 go 중복으로 설치된 패키지들을 파악해서 한쪽은 제거하자.

golang gomaxprocs in k8s

golang garbage collector(GC) 는 데이터 무결성을 위해 stop-the-world(일시정지)가 필요하다.

linux 스케줄러 Completely Fair Scheduler(CFS) 에서 프로세스를 cpu(core) 시간에 할당한다.
golang 은 container(linux cfs 기반)의 cpu (시간)제한을 인지하지 못해 일시정지가 발생할 수 있다.

GOMAXPROCS 를 k8s cpu limit 와 일치 시키면 gc 로 인한 일시정지를 줄 일 수 있다.
GOMAXPROCS 를 k8s cpu limit 와 일치시키기
ubuer automaxprocs 를 golang main 에 import 하면 되지만 GOMEMLIMIT 는 지원하지 않는다.

대신 환경변수로 GOMAXPROCS, GOMEMLIMIT 로 pod > resource > limits 를 설정하면 된다.

golang main() 에 다음을 추가해 프로그램 시작시 GOMAXPROCS, GOMEMLIMIT 값을 찍어보자.
기본적으로 최대 core 개수가 찍힌다.
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
fmt.Printf("GOMEMLIMIT: %d\n", debug.SetMemoryLimit(-1))

이제 deployment 에 다음과 같이 GOMAXPROCS, GOMEMLIMIT 환경변수를 적용하면
pod(container)가 새로 시작되고 golang 프로그램의 GOMAXPROCS, GOMEMLIMIT 에 반영된다.
spec:
  template:
    spec:
      containers:
      - name: ysoftman-app
        resources:
          requests:
            cpu: "2000m"
            memory: "2048Mi"
          limits:
            cpu: "2000m"
            memory: "2048Mi"
        env:
        - name: GOMEMLIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
        - name: GOMAXPROCS
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu

golang disassembly

# golang binary disassembly
go tool objdump -S exec_file_path

# 위 방식은 수백메가 이상의 파일을 생성하며 보기도 쉽지 않다.
# lensm 을 사용해보자.
# 설치
go install loov.dev/lensm@main

# 사용하기
# -watch  auto reload executable
# -filter filter the functions by regexp
# main(hello_world.go) 바이너리 assembly 보기
lensm -watch -filter main main

# 요렇게 창이 뜨고 소스 라인 별로 어셈블리 코드를 편하게 확인할 수 있다.

golang package name

# golang 에서 package(module)명은 지을 때 소문자로 모두 붙여쓰라고 한다.
# camel_case, snake_case 등은 사용하지 말고
computeServiceClient
priority_queue

# 다음과 같이 소문자로만 의미있게 축약하는것이 좋다고 한다.
strconv (string conversion)
syscall (system call)
fmt (formatted I/O)

# k8s 소스에서 package 를 검색해봤다.(정규식에서 길이 조건을 쓸수 없다.)
# syscall 처럼 의미 있게 축약되면 좋지만 client, pod 이런 단어가 조합되는 경우 축약이 힘들어 보인다.

# k8s 소스를 다운받아 package 이름 긴것들을 확인해 보면 이렇다.
# 소문자까지는 좋은데 넘 길면 갠적으로 snake_case 가 더 눈에 잘 들어온다.
rg -IN "^package [[:alpha:]]{10,50}$" | awk '{print length, $0}' | sort -r | uniq | head -10
40 package validatingadmissionpolicybinding
39 package validatingadmissionpolicystatus
38 package validatingwebhookconfiguration
37 package prereleaselifecyclegenerators
37 package externalaccountauthorizeduser
36 package storageobjectinuseprotection
36 package mutatingwebhookconfiguration
34 package prioritylevelconfiguration
34 package extendedresourcetoleration
34 package clusterauthenticationtrust

keep swag fmt

# .go 파일에서 swag comment 작성 후 swag fmt 하면 다음과 같이 주석을 보기 좋게 간격을 띄워 준다.

# 그런데 vim, vscode 등에서 저장하면 gofmt 이 동작하며 다음과 같이 주석 앞쪽을 공백으로 바꾸면서 간격이 흐트러진다.
# 이상태에선 swag fmt 해도 포맷팅이 되지 않는다.
# 이상태에서 탭 부분을 수정 후 swag fmt 하면 포맷팅돼 위 화면처럼 된다.

# vim 등에서 저장(gofmt 으로 주석이 포맷팅)시 swag fmt 부분을 유지하려면
# 다음과 같이 맨위 주석과 공백 주석을 추가하면 gofmt 해도 이 swag 주석 부분이 변경되지 않는다.

#####

# default, enum 등의 속성 사용 예시
// @Param   enumstring  query     string     false  "string enums"       Enums(A, B, C)
// @Param   enumint     query     int        false  "int enums"          Enums(1, 2, 3)
// @Param   enumnumber  query     number     false  "int enums"          Enums(1.1, 1.2, 1.3)
// @Param   string      query     string     false  "string valid"       minlength(5)  maxlength(10)
// @Param   int         query     int        false  "int valid"          minimum(1)    maximum(10)
// @Param   default     query     string     false  "string default"     default(A)
// @Param   example     query     string     false  "string example"     example(string)
// @Param   collection  query     []string   false  "string collection"  collectionFormat(multi)
// @Param   extensions  query     []string   false  "string collection"  extensions(x-example=test,x-nullable)

golangci-lint

# vim 에서 golang generic(1.18)를 사용하는데 lint 에러가 많이 발생한다.
# vim > vim-go > golint 를 사용하고 있고 golint 바이너리는 업데이트 해도 다음과 같은 메시지가 발생한다.
golint ./...
main.go:12:8: expected ';', found '|' (and 4 more errors)

# vim > ale > gofmt 도 다음과 같은 에러 메시지를 발생한다.
gofmt .
main.go:12:8: expected ';', found '|'
main.go:12:10: illegal character U+007E '~'
main.go:16:2: expected '}', found 'return'
main.go:20:2: expected declaration, found result

https://github.com/golang/lint 가보니 2021년에 deprecated 돼 관리가 안되고 있었다.
# gofmt 바이너리는 2018년에 생성된것을 사용하고 있었다.
type gofmt
gofmt is /Users/ysoftman/workspace/gopath/bin/gofmt --> 2018년도
gofmt is /opt/homebrew/bin/gofmt --> ../Cellar/go/1.22.1/bin/gofmt

# golint 는 삭제하고
rm -f $(which golint)

# 2018년도 gofmt 삭제
rm -f /Users/ysoftman/workspace/gopath/bin/gofmt

# 대안으로 https://github.com/golangci/golangci-lint 를 사용하면 된다.
# 설치
brew install golangci-lint

# 실행
golangci-lint run ./...

# revive 사용시 설치
go install github.com/mgechev/revive@latest

# vim-go 사용시
":GoMetaLinter 명령실행시 동작할 커맨드
let g:go_metalinter_command = "golangci-lint"
"최신 golangci-lint 에서 --deadline 옵션이 --timeout 으로 변경됨
"vim-go 에서는 아직 deadline 을 사용하고 있어 주석처리함
"let g:go_metalinter_deadline = "5s"
"golangci-lint 에서 활성화할 항목
"vet -> govet 으로 바뀜
let g:go_metalinter_enabled = ['govet', 'revive', 'errcheck']

# ale 사용시
"vim-go 와 달리 linter 커맨드를 입력하지 않아도 golangci-lint 결과가 코드에 자동으로 표시된다
let g:ale_linters = {
\ 'python': ['flake8', 'pylint'],
\ 'javascript': ['eslint'],
\ 'go': ['golangci-lint', 'gofmt']
\}


gorm join table

# 다음과 department:member 가 M:N 관계인 테이블이 있을때
[departments table]
id uint
name string

[members table]
id uint
name string

[departments_members table](관계 매핑테이블/join table)
id uint
department_key uint
member_key uint
val1 string
val2 string

# 조인 테이블은 다음과 같이 FK 를 설정했다.
`FK_MEMBERS` FOREIGN KEY (`member_key`) REFERENCES `members` (`id`)
`FK_DEPARTMENTS` FOREIGN KEY (`department_key`) REFERENCES `departments` (`id`),

# golang gorm 사용시
# 사용 테이블 이름 설정
type Tabler interface { TableName() string}
func (Department) TableName() string  { return "departments" }
func (Member) TableName() string  { return "members" }

# 부서 조회시 부서에 속한 멤버들을 조회를 한다고 했을때
# many2many 로 조인테이블 departments_members 을 참고하도록 했고
type Member struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  Departments []Department `gorm:"many2many:departments_members;" json:"departments,omitempty"` 
}
type Department struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  Members []Member `gorm:"many2many:departments_members; "json:"members,omitempty"`
}

# 다음과 preload 로 멤버가 포함된 부서를 조회를 하면 
err := ysoftmanMysql.Db.Model(&db.Department{}).Preload("Members").Find(&department, "name = ?", name).Error

# 다음과 같이 department_id 를 알 수 없다고 나온다.
Error 1054 (42S22): Unknown column 'departments_members.department_id' in 'where clause'

# 기본적으로 foreignkey로 모델명(struct)+pk 를 붙인 이름(department_id)필드를 사용한다.
# departments_members 의 필드명을 다음과 같이 바꾸면 된다.
department_key -> department_id
member_key -> member_id

# 참고
# github.com/jinzhu/gorm -> 1.x
# gorm.io/gorm -> 2.x

#####

# departments_members 테이블 자체에 있는 val1,val2 필드고 같이 조회 하기
func (DepartmentsMembers) TableName() string { return "departments_members" }

type Member struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  # DepartmentMembers 에서 MemberID 로 조회 할 수 있도록 한다.
  DepartmentMembers []DepartmentMembers `gorm:"foreignkey:MemberID" json:"users,omitempty"`
}

type Department struct {
  ID   uint64  `gorm:"primary_key;auto_increment" json:"id"`
  Name string  `gorm:"unique;size:100;not null" json:"name"`
  # DepartmentMembers 에서 DepartmentID 로 조회 할 수 있도록 한다.
  DepartmentMembers []DepartmentMembers `gorm:"foreignkey:DepartmentID" json:"users,omitempty"`
}

# DepartmentsMembers 데이터추가고 이곳에서 Member, Department 를 담을 수 있도록 한다.
type DepartmentsMembers struct {
  ID       uint64  `gorm:"primary_key;auto_increment" json:"id"`
  MemberID   uint64  `gorm:"";auto_increment" json:"user_id,omitempty"`
  DepartmentID uint64  `gorm:"" json:"user_id,omitempty"`
  Member      *Member  `json:"member,omitempty"`
  Department  *Department  `json:"department,omitempty"`
}

# Department 조회시 멤버 정보가 포함해서 조회
# DepartmentsMember.Member 를 한번 더 프리로드 한다.
err := ysoftmanMysql.Db.Preload("DepartmentsMember").Preload("DepartmentsMember.Member").Find(&project, "project_id = ?", id).Error

golangci-lint cpu usage

# vscode 로 golang 을 오픈하면 갑자기 컴이 버벅거린다.
# 아래 프로세스가 CPU 를 모두 사용하고 있었다.
golangci-lint run --print-issued-lines=false --out-format=colored-line-number --issues-exit-code=0

# golangci-lint 커맨드 설명을 보면 기본 16core 를 사용하고 있다.
-j, --concurrency int           Concurrency (default NumCPU) (default 16)

# vscode 설정을 요렇게 변경해보자.
// file 저장시 lint 수행, lint 결과 problems 에 표시
"go.lintOnSave": "file",
"go.lintTool": "golangci-lint",
"go.lintFlags": [
  "--allow-parallel-runners",
  "--concurrency",
  "4"
],

docker build image apt-update error

# 잘 되던 docker 이미지 빌드 중 Dockerfile 에서 패키지 업데이트시
RUN apt-get update
...
#6 10.23 Reading package lists...
#6 10.24 W: The repository 'http://security.debian.org/debian-security stretch/updates Release' does not have a Release file.
#6 10.24 W: The repository 'http://deb.debian.org/debian stretch Release' does not have a Release file.
#6 10.24 W: The repository 'http://deb.debian.org/debian stretch-updates Release' does not have a Release file.
#6 10.24 E: Failed to fetch http://security.debian.org/debian-security/dists/stretch/updates/main/binary-amd64/Packages  404  Not Found
#6 10.24 E: Failed to fetch http://deb.debian.org/debian/dists/stretch/main/binary-amd64/Packages  404  Not Found
#6 10.24 E: Failed to fetch http://deb.debian.org/debian/dists/stretch-updates/main/binary-amd64/Packages  404  Not Found
#6 10.24 E: Some index files failed to download. They have been ignored, or old ones used instead.
------
executor failed running [/bin/sh -c apt-get update && apt-get install -y net-tools htop lsof wget curl rsync vim tar man-db traceroute]: exit code: 100

# 기본 이미지를 변경하면 잘 동작한다. 
FROM golang:1.17-stretch  -> FROM golang:1.19

# 참고로 데이안 계열 이미지 suffix 로 붙는 이름(코드네임)
-없는경우 debian latest
-bullseye: debian 11 
-buster: debian 10.4
-stretch: debian 9
-jessie: debian 8

mongodb-go-driver cursor not found error

# mongo go client v1.9.1 (https://github.com/mongodb/mongo-go-driver) 로 documents 조회(find)를 다음과 같이 구현했다.
cursor, err := c.collection.Find(context.TODO(), filter)
if err != nil {
    return err
}
defer cursor.Close(context.TODO())
return cursor.All(context.TODO(),  results)

# 여러개의 조회 요청이 동시에 들어오면 일부 요청 결과에 다음 에러가 발생했다.
(CursorNotFound) Cursor not found (namespace:

# 에러를 발생하는 mongo-go-driver 부분
# https://github.com/mongodb/mongo-go-driver/blob/4f06ad2489b73cd1dbcebb5e7df09b77cb643be1/mongo/cursor.go#L266

# document 수가 50개로 적은 컬렉션에 대해서 조회시 발생하지 않고,
# document 수가 156개인 컬렉션에서 발생했다.
 
# 커서를 읽기(cursor.All)전에 현재 커서에 있는 document 를 찍어보면 101로 보인다.
cursor.RemainingBatchLength()

# 이유는 default batchSize=101 이기 때문이다.
# 인덱스 없는 정렬시에는 전체 document 를 로딩한다고 한다.
```
find() and aggregate() operations have an initial batch size of 101 documents by default. Subsequent getMore operations issued against the resulting cursor have no default batch size, so they are limited only by the 16 megabyte message size.

For queries that include a sort operation without an index, the server must load all the documents in memory to perform the sort before returning any results.
```

# Find 옵션으로 batch 크기를 다음과 같이 설정했다.
opts := options.Find()
opts = opts.SetBatchSize(500)
cursor, err := c.collection.Find(context.TODO(), filter, opts)

# cursor.RemainingBatchLength()를 출력해보면 156 이다.
# 위와 같이 한번에 전체 documents 를 가져오니 cursor not found 에러가 발생하지 않았다.
# batchSize 가 전체 documents 보다 작으면 계속 batchSize 만큼 조회해서 가져오는것으로 보인다.(실제 batchSize 101일때 1번 find 최종 조회 결과는 156개의데이터를 모두 가져왔다.)
# 하지만 이 과정에서 동시에 조회가 실행될 경우 서버로 부터 커서를 찾을 수 없다는 응답(헤더)를 받는것으로 보인다.

# 참고
# default batchSize 로 인한 지연
# batchSize 설정값에 따른 성능 결과

add timezone in alpine

# golang 에서 타임존이 변경된 time 사용을 위해 time.LoadLocation 를 사용했다.
loc, _ := time.LoadLocation("Asia/Seoul")
t = t.In(loc)

# 로컬에서는 동작했지만,
# 배포된 도커에서는 loc 를 찾을 수 없어 panic 이 발생하고 있었다.
[PANIC RECOVER] time: missing Location in call to Time.

# 컨테이너 사이즈를 줄이기 위해 alpine 이미지를 사용하고 있었는데,
# LoadLocation 가 참조하는 zoneinfo 데이터가 존재하지 않아 발생한 문제였다.

# dockerfile 에 tzdata 를 설치를 추가하자.
RUN apk add tzdata

# 이제 다음과 같은 위치에 timezone 데이터 값이 추가되고,
# time.LoadLocation 도 사용할 수 있다.
/usr/share/zoneinfo/Asia/Seoul

golang vim-go GOBIN error

# go 1.19.2 버전 설치 후 .go 파일을 열때 다음과 같은 에러가 발생했다.
# :messages 로 확인해보면
vim-go: 'go env GOBIN' failed
vim-go: could not determine appropriate working directory for gopls
FileNotFoundError: [Errno 2] No such file or directory: '/Users/ysoftman/.vim/plugged/youcompleteme/third_party/ycmd/third_party/go/bin/gopls'

# vim plug 를 업데이트해도 에러가 발생한다.
:PlugUpdate

# 참고로 vim-go 최신 소스를 보면 go env gobin > $GOBIN > GOPATH./bin 순으로 찾고 있다.

# 다음과 같이 설정해도 에러가 발생한다.
# GOBIN (go install 시 실행 binary 설치 경로)
go env -w GOBIN=/Users/ysoftman/workspace/gopath/gopath/bin
go env GOBIN

# 원인은 go 실행 자체가 안되는것이 문제였다.
# go 신규 버전 설치시 macOS(darwin)환경인데 linux 바이너리파일을 설치한것이 문제였다.
# go1.19.2.linux-amd64.tar.gz -> go1.19.2.darwin-amd64.tar.gz 로 변경해서 설치해고 go 가 실행된다.
# 이제 GOBIN 을 설정하지 않아도(GOPATH/bin 로 찾는다.) 에러가 없다.
go env -w GOBIN=

# vim-plug 라면 go binary 업데이트도 해주자.
:GoUpdateBinaries

# ycmd 에러 관련해서는 다음과 같이 설치하면 된다.
cd ~/.vim/plugged/youcompleteme/third_party/ycmd/
git checkout master
git pull
git submodule update --init --recursive
./build.py --go-completer