How to preload with nullable ForeignKey - go

I have the following example:
type PostModel struct {
Id int64 `gorm:"primary_key;AUTO_INCREMENT;column:id"`
UserId *int64 `gorm:"column:userId;"`
User UserModel `gorm:"ForeignKey:Id;AssociationForeignKey:UserId"`
}
type UserModel struct {
Id int64 `gorm:"primary_key;AUTO_INCREMENT;column:id"`
}
when I use
db.Model(&model.PostModel{}).Where(where).Preload("User").Find(&post)
I got a Correct reply, but it has something wrong.
SELECT * FROM `user` WHERE (`id` IN (?,?,?,?,?,?,?,?,?,?))[0xc00023dce0 0xc00023dda0 0xc00023d9e0 0xc00023d920 0xc00023daa0 0xc00023db60 0xc00023dc20 0xc00023de60 0xc00023df20 0xc00023d790] 1
All userid are 1, but in gorm sql,it use pointer address to replace number. like 0xc00023dce0 0xc00023dda0 0xc00023d9e0 0xc00023d920 0xc00023daa0 0xc00023db60 0xc00023dc20 0xc00023de60 0xc00023df20 0xc00023d790.
How to fix it?

I wrote a sample code as you described and put Gorm in log mode to see the SQLs but it prints correct SQLs:
[2020-03-29 21:29:42] [0.17ms] SELECT * FROM "post_models"
[4 rows affected or returned ]
[2020-03-29 21:29:42] [0.18ms] SELECT * FROM "user_models" WHERE ("id" IN (1,2,1,3))
[3 rows affected or returned ]
As you see queries are correct.
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"log"
"os"
)
type PostModel struct {
Id int64 `gorm:"primary_key;column:id"`
UserId *int64 `gorm:"column:userId;"`
User UserModel `gorm:"ForeignKey:UserId"`
}
type UserModel struct {
Id int64 `gorm:"primary_key;column:id"`
}
func preloadingSample() error {
_ = os.Remove("test.db") // Remove file to make sure DB is empty
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
return fmt.Errorf("open DB failed: %w", err)
}
defer db.Close()
db.LogMode(true)
err = db.AutoMigrate(
&PostModel{},
&UserModel{},
).Error
if err != nil {
return fmt.Errorf("migration failed: %w", err)
}
// Put some sample data in DB
sampleUsers := []UserModel{
{},
{},
{},
}
for idx := range sampleUsers {
err = db.Create(&sampleUsers[idx]).Error
if err != nil {
return fmt.Errorf("failed to create: %w", err)
}
}
samplePosts := []PostModel{
{User: sampleUsers[0]},
{User: sampleUsers[1]},
{User: sampleUsers[0]},
{User: sampleUsers[2]},
}
for idx := range samplePosts {
err = db.Create(&samplePosts[idx]).Error
if err != nil {
return fmt.Errorf("failed to create: %w", err)
}
}
var posts []PostModel
err = db.Preload("User").Find(&posts).Error
if err != nil {
return fmt.Errorf("error in find: %w", err)
}
fmt.Printf("%+v\n", posts)
return nil
}
func main() {
err := preloadingSample()
if err != nil {
log.Fatal(err)
}
}

Related

golang test error "cannot use as error value in assignment: *"gorm.io/gorm".DB does not implement error"

I am trying to test user registration with echo but I get an error.
Here is the specific error.
cannot use r.Create(id, name, password, email) (value of type *"gorm.io/gorm".DB) as error value in assignment: *"gorm.io/gorm".DB does not implement error
How can I resolve this error to make a correct test?
package test
import (
"regexp"
"testing"
sqlmock "github.com/DATA-DOG/go-sqlmock"
"github.com/...../model"
"github.com/jinzhu/gorm"
)
func getDBMock() (*gorm.DB, sqlmock.Sqlmock, error) {
db, mock, err := sqlmock.New()
if err != nil {
return nil, nil, err
}
gdb, err := gorm.Open("postgres", db)
if err != nil {
return nil, nil, err
}
return gdb, mock, nil
}
func TestCreate(t *testing.T) {
db, mock, err := getDBMock()
if err != nil {
t.Fatal(err)
}
defer db.Close()
db.LogMode(true)
r := model.Repository{DB: db}
id := "2222"
name := "BBBB"
password := "mfsdmSD34"
email := "dmvsdmvo#.com"
// Mock設定
mock.ExpectQuery(regexp.QuoteMeta(
`INSERT INTO "users" ("id","name","passowrd","email") VALUES ($1,$2,$3,$4)
RETURNING "users"."id"`)).WithArgs(id, name,password,email).
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(id))
// 実行
err = r.Create(id, name,password,email)
if err != nil {
t.Fatal(err)
}
}
package model
type User struct {
ID int `json:"id" gorm:"praimaly_key"`
Name string `json:"name"`
Password string `json:"password"`
Email string `json:"email"`
}
func CreateUser(user *User) {
db.Create(user)
}
func FindUser(u *User) User {
var user User
db.Where(u).First(&user)
return user
}
package model
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var db *gorm.DB
func init() {
var err Error
dsn := "user:pass#tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
db.AutoMigrate(&User{})
db.AutoMigrate(&Todo{})
}

How to unmarshal/parse a request using Go correctly?

How to parse a curl request response in Golang correctly? I have tried the following where I send a request to an api and its response is:
{
"Certificates": [
{
"Name": "some-name.here",
.......
}
],
"DataRange": "Certificates 1 - 1",
"TotalCount": 1
}
Now I want to use the Name in the Certificates in a string variable. i.e match. Before even I get to the looping through of the response, I get the error: json: cannot unmarshal object into Go value of type []program.Item. This error is coming from json.Unmarshal function where I pass my []bytes and the struct to use the bytes. Am I doing this correctly?
type Item struct {
Certificates []CertificatesInfo
}
type CertificatesInfo struct {
Name string
}
func main() {
url := .....
req, err := http.NewRequest("GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
// handle err
continue
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
continue
}
var ItemInfo []Item
if err := json.Unmarshal(bodyBytes, &ItemInfo); err != nil {
return nil, fmt.Errorf("failed to parse %v", err)
}
for _, infos := range ItemInfo {
for _, names := range infos.Certificates {
infraId:= names.Name
match:= display_name
}
}
}
There's a mismatch between your sample JSON response data (a single object containing a list of certificates), and your code, which is expecting a list of objects, each of which contain a list of certificates).
Assuming that your JSON example is correct, this bit:
var ItemInfo []Item
if err := json.Unmarshal(bodyBytes, &ItemInfo); err != nil {
. . .
should probably be
var itemInfo Item
if err := json.Unmarshal(bodyBytes, &ItemInfo); err != nil {
. . .
Works on my machine:
https://go.dev/play/p/8FqIif1zzsQ
package main
import (
"encoding/json"
"fmt"
)
func main() {
resp := Item{}
err := json.Unmarshal([]byte(data), &resp)
if err != nil {
fmt.Print(err)
panic(err)
}
fmt.Printf("resp.DataRange: %s\n", resp.DataRange)
fmt.Printf("resp.TotalCount: %d\n", resp.TotalCount)
fmt.Printf("len(resp.Certificates: %d\n", len(resp.Certificates))
for i, c := range resp.Certificates {
fmt.Printf("resp.Certificates[%d].Name: %s\n", i, c.Name)
}
}
type Item struct {
Certificates []CertificatesInfo
DataRange string
TotalCount int
}
type CertificatesInfo struct {
Name string
}
const data = `
{
"Certificates": [
{ "Name": "alpha" },
{ "Name": "bravo" },
{ "Name": "charlie" }
],
"DataRange": "Certificates 1 - 1",
"TotalCount": 3
}
`

I want to create Access Rights by using MSPID for different organisations

My idea to do this was to use the MSPID. So I would get the MSPID of the organisation, if it is the wrong MSPID then it will return an error message and if it is the right MSPID then continue. In my code I only did it in the DeleteAsset function however I would like to do it in the CreateAsset, UpdateAsset, TransferAsset and DeleteAsset functions.
The problem is that when I go to use the CreateAsset function on the command line it gives me this error:
peer chaincode invoke "${TARGET_TLS_OPTIONS[#]}" -C mychannel -n basic -c '{"function":"CreateAsset","Args":["Asset1","Andrew Faure","20","None","O+", "Penicilin", "None", "Family Doctor", "Fractured Leg", "10/05/2022"]}'
Error: endorsement failure during invoke. response: status:500 message:"error in simulation: failed to execute transaction 9446e0c320832b92f06ea56f577a29e9a5ec94c276329cb1d31bb8a85581138d: could not launch chaincode basic_1.0:605aa3a69a8107c9c7b5fc22072554235d3ea827b0eefbffae29f0c9998bf0e6: chaincode registration failed: container exited with 2"
Without the MSPID the system works fine. However, I need the MSPID in order to create access rights to organisations.
I'm new to Hyperledger and Go lang. Would really appreciate it if anyone can help
package chaincode
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}
type Asset struct {
ID string `json:"ID"`
Owner string `json:"Owner"`
Age int `json:"Age"`
MedicalFamilyHistory string `json:"MedicalFamilyHistory"`
BloodType string `json:"BloodType"`
Allergies string `json:"Allergies"`
Medication string `json:"Medication"`
DataSharedWith string `json:"DataSharedWith"`
CurrentIssue string `json:"CurrentIssue"`
Date string `json:"Date"`
}
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id
string, owner string, age int, medicalFamilyHistory string, bloodType string,
allergies string, medication string, dataSharedWith string, currentIssue
string, date string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}
asset := Asset{
ID: id,
Owner: owner,
Age: age,
MedicalFamilyHistory: medicalFamilyHistory,
BloodType: bloodType,
Allergies: allergies,
Medication: medication,
DataSharedWith: dataSharedWith,
CurrentIssue: currentIssue,
Date: date,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id
string) (*Asset, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("the asset %s does not exist", id)
}
var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}
return &asset, nil
}
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id
string, owner string, age int, medicalFamilyHistory string, bloodType string,
allergies string, medication string, dataSharedWith string, currentIssue string, date
string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
// overwriting original asset with new asset
asset := Asset{
ID: id,
Owner: owner,
Age: age,
MedicalFamilyHistory: medicalFamilyHistory,
BloodType: bloodType,
Allergies: allergies,
Medication: medication,
DataSharedWith: dataSharedWith,
CurrentIssue: currentIssue,
Date: date,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, stub
shim.ChaincodeStubInterface, id string) error {
msp, err := s.GetMSPID(stub)
if err != nil {
return err
}
// The error is with "Org1MSP" becuase I found out that mspid is Org1MSP
if msp != "org1MSP" {
return fmt.Errorf("Wrong Organisation, this organisation does not have access
to this function")
}
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
return ctx.GetStub().DelState(id)
}
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id
string) (bool, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return assetJSON != nil, nil
}
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id
string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return "", err
}
oldOwner := asset.Owner
asset.Owner = newOwner
assetJSON, err := json.Marshal(asset)
if err != nil {
return "", err
}
err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
}
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface)
([]*Asset, error) {
// range query with empty string for startKey and endKey does an
// open-ended query of all assets in the chaincode namespace.
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var assets []*Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, &asset)
}
return assets, nil
}
func (s *SmartContract) GetMSPID(stub shim.ChaincodeStubInterface) (string, error) {
// Get the Client ID object
clientId, err := cid.New(stub)
if err != nil {
// Handle error
}
mspid, err := clientId.GetMSPID()
if err != nil {
// Handle error
}
// mspId, err := cid.GetMSPID(stub)
// if err != nil {
// return err
// }
// The error is with "Org1MSP" becuase I found out that mspid is Org1MSP
// if mspId != "Org1MSP" {
// return fmt.Errorf(mspId)
// }
return mspid, nil
}
Please look into below fabric sample chaincode. In this smart contract , they are validating MSPID.
https://github.com/hyperledger/fabric-samples/blob/main/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go[![enter image description here]1]1

Inadvertent multiple returns

I'm building an application where I get muslim prayer data from multiple sources. The first being S3, the second being aladhan (a public api). I only want to get data from aladhan if it's not available in S3. If I do have to get the data from the public source then I upload it to my s3.
Here is the code:
This is my interface loop code. I've put in print statements to show that I'm running into the return statement twice, once with data in my return struct, the second time the struct is nil.
// prayeriface.go
package prayer
import (
"fmt"
)
type MonthPrayerIface interface {
GetMonthPrayer(input *LookupInput) (*PreCurrNextMonthPrayer, error)
}
type PreCurrNextMonthPrayer struct {
custData *LookupInput
CurrentMonthData *PCal
PreviousMonthData *PCal
NextMonthData *PCal
prayers []MonthPrayerIface
}
func (p *PreCurrNextMonthPrayer) GetMonthPrayers() (*PreCurrNextMonthPrayer, error) {
var err error
var monthlyData *PreCurrNextMonthPrayer
defer func() {
fmt.Printf("return monthlyData address & value = %p %v\n", monthlyData, monthlyData)
}()
for k, data := range p.prayers {
fmt.Printf("loop = %v, data= %T %v\n", k, monthlyData, monthlyData)
monthlyData, err = data.GetMonthPrayer(p.custData)
fmt.Printf("\terr= %v\n", err)
fmt.Printf("\tmonthlyData= %p %v\n", monthlyData, monthlyData)
if err == nil {
fmt.Printf("loop-return: err == nil \n")
return monthlyData, nil
}
}
if err == nil {
fmt.Printf("post-loop:\n")
fmt.Printf("\tmonthlyData= %p %v\n", monthlyData, monthlyData)
return monthlyData, nil
}
return nil, fmt.Errorf("unable to get prayer data from all sources %s", err)
}
func NewMonthPrayer(input *LookupInput, prayers ...MonthPrayerIface) (*PreCurrNextMonthPrayer, error) {
var err error
t := &PreCurrNextMonthPrayer{
custData: input,
prayers: prayers,
}
t, err = t.GetMonthPrayers()
if err != nil {
return nil, err
}
return t, nil
}
As you can see, I'm looping over an interface struct method called GetMonthPrayer
This is my s3 source
// s3.go
package prayer
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"io"
awsservices "prayer-times/src/aws"
)
// S3Store s3 storage object for prayer calendars
type S3Store struct {
data *PCal
}
// GetMonthPrayer retrieves prayer data from s3, otherwise from aladhan
func (s *S3Store) GetMonthPrayer(input *LookupInput) (*PreCurrNextMonthPrayer, error) {
mPrayer := new(PreCurrNextMonthPrayer)
fmt.Println("attempting to retrieve prayer data from s3")
s3Client := awsservices.NewS3Service()
pMonthInput := &LookupInput{
Country: input.Country,
ZipCode: input.ZipCode,
custTime: input.custTime.AddDate(0, -1, 0),
}
nMonthInput := &LookupInput{
Country: input.Country,
ZipCode: input.ZipCode,
custTime: input.custTime.AddDate(0, 1, 0),
}
// s3Pdata retrieves data from S3 and
s3pData := func(input *LookupInput) (*PCal, error) {
pCalendar := new(PCal)
data, err := s3Client.GetObject(&s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(
fmt.Sprintf(
"%s/%d/%d/%d",
input.Country,
input.ZipCode,
input.custTime.Year(),
input.custTime.Month())),
})
if err != nil {
return nil, err
}
if data == nil {
return nil, errors.New("error data from s3 is nil")
}
defer func() {
err := data.Body.Close()
if err != nil {
fmt.Printf("unable to close s3 body: %s", err)
}
}()
s3buf := bytes.NewBuffer(nil)
if _, err := io.Copy(s3buf, data.Body); err != nil {
return nil, err
}
dataBytes := s3buf.Bytes()
decoder := json.NewDecoder(bytes.NewReader(dataBytes))
err = decoder.Decode(&pCalendar)
if err != nil {
fmt.Printf("unable to decode json: %s", err)
}
return pCalendar, nil
}
aladhanData := new(AladhanStore)
getAladhanData := func(input *LookupInput) (*PreCurrNextMonthPrayer, error) {
data, err := aladhanData.GetMonthPrayer(input)
if err != nil {
return nil, err
}
return data, nil
}
// Get current data from s3, if not s3, then get all three from aladhan
cMonthS3Data, err := s3pData(input)
pMonthS3Data, err := s3pData(pMonthInput)
nMonthS3Data, err := s3pData(nMonthInput)
if err != nil {
adata, err := getAladhanData(input)
if err != nil {
fmt.Printf("err: %s", err)
return nil, err
}
return adata, nil
}
mPrayer.CurrentMonthData = cMonthS3Data
// Get previous month data from s3, if not s3, then get all three from aladhan
mPrayer.PreviousMonthData = pMonthS3Data
// Get next month data from s3, if not s3, then get all three from aladhan
mPrayer.NextMonthData = nMonthS3Data
return mPrayer, nil
}
Here is my aladhan source
// aladhan.go
package prayer
import (
"bytes"
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"golang.org/x/sync/errgroup"
"io"
"log"
awsservices "prayer-times/src/aws"
"prayer-times/src/urljsonoutput"
"prayer-times/src/zipcoordinates"
)
var (
aladhanURL string = "https://api.aladhan.com/v1/calendar?"
)
// PCal contains the prayer times of the month as well as the return code
type PCal struct {
Code int `json:"code"`
Status string `json:"status"`
Data []struct {
Timings DailyPrayers
}
}
/*
AladhanData returns the total monthly prayers of given month, coordinates, and zip from aladhan.
https://api.aladhan.com/v1/calendar?latitude=51.508515&longitude=-0.1254872&method=1&month=4&year=2017
*/
func AladhanData(input *LookupInput) *PCal {
coordinates := zipcoordinates.HereCoordinates(&zipcoordinates.GeoLocationInput{
PostalCode: input.ZipCode,
CountryCode: input.Country,
})
respStruct := new(PCal)
_, err := urljsonoutput.GetURLJSON(fmt.Sprintf(
"%slatitude=%v&longitude=%v&method=%v&month=%v&year=%v",
aladhanURL,
coordinates.Items[0].Position.Latitude,
coordinates.Items[0].Position.Longitude,
input.Method,
int(input.custTime.Month()),
input.custTime.Year()), respStruct)
if err != nil {
log.Fatalf("unable to pull monthly prayer data %v", err)
}
return respStruct
}
// AladhanStore struct to interact with interface for GetMonthPrayer
type AladhanStore struct {
data *PCal
}
// GetMonthPrayer Pulls prayer data from aladhan
func (a *AladhanStore) GetMonthPrayer(input *LookupInput) (*PreCurrNextMonthPrayer, error) {
mPrayer := new(PreCurrNextMonthPrayer)
// Return prayer data from aladhan
custPMonthTime := input.custTime.AddDate(0, -1, 0)
pMonthLookupInput := new(LookupInput)
pMonthLookupInput.custTime = custPMonthTime
pMonthLookupInput.ZipCode = input.ZipCode
pMonthLookupInput.Country = input.Country
custNMonthTime := input.custTime.AddDate(0, 1, 0)
nMonthLookupInput := new(LookupInput)
nMonthLookupInput.custTime = custNMonthTime
nMonthLookupInput.ZipCode = input.ZipCode
nMonthLookupInput.Country = input.Country
prayerData := AladhanData(input)
pMonthPData := AladhanData(pMonthLookupInput)
nMonthPData := AladhanData(nMonthLookupInput)
// Save prayer data into io.Reader to save to s3
var Marshal = func(data interface{}) (io.ReadSeeker, error) {
mdata, err := json.MarshalIndent(data, "", "\t")
if err != nil {
return nil, err
}
return bytes.NewReader(mdata), nil
}
rmData, err := Marshal(prayerData)
pRmData, err := Marshal(pMonthPData)
nRmData, err := Marshal(nMonthPData)
if err != nil {
return nil, err
}
// Save prayer data into s3
g := new(errgroup.Group)
s3Upload := func(rawData *io.ReadSeeker, input *LookupInput) func() error {
return func() error {
s3Client := s3manager.NewUploaderWithClient(awsservices.NewS3Service())
_, err = s3Client.Upload(&s3manager.UploadInput{
Bucket: aws.String(bucket),
Key: aws.String(
fmt.Sprintf(
"%s/%d/%d/%d",
input.Country,
input.ZipCode,
input.custTime.Year(),
int(input.custTime.Month()))),
Body: *rawData,
})
if err != nil {
return err
}
return nil
}
}
g.Go(s3Upload(&pRmData, pMonthLookupInput))
g.Go(s3Upload(&rmData, input))
g.Go(s3Upload(&nRmData, nMonthLookupInput))
if err := g.Wait(); err == nil {
mPrayer.PreviousMonthData = pMonthPData
mPrayer.CurrentMonthData = prayerData
mPrayer.NextMonthData = nMonthPData
return mPrayer, nil
}
return nil, err
}
Here is my test file.
func TestPrayer(t *testing.T) {
p, err := NewMonthPrayer(
&input,
&S3Store{},
&AladhanStore{},
)
if err != nil {
t.Errorf("error: %s", err)
}
data, err := p.GetMonthPrayers()
if err != nil {
t.Errorf("error: %s", err)
}
t.Logf("Test address: %p", data)
t.Logf("data THIS SHOULDN'T BE NIL: %v", data)
t.Logf("ERROR: %s", err)
}
These are my results. Ignore the pass result, the data is first not nil and second nil.
=== RUN TestPrayer
loop = 0, data= *prayer.PreCurrNextMonthPrayer <nil>
attempting to retrieve prayer data from s3
err= <nil>
monthlyData= 0xc000131180 &{<nil> 0xc0002612f0 0xc00051e780 0xc00011cea0 []}
loop-return: err == nil
return monthlyData address & value = 0xc000131180 &{<nil> 0xc0002612f0 0xc00051e780 0xc00011cea0 []}
post-loop:
monthlyData= 0x0 <nil>
return monthlyData address & value = 0x0 <nil>
prayer_test.go:53: Test address: 0x0
prayer_test.go:55: data THIS SHOULDN'T BE NIL: <nil>
prayer_test.go:56: ERROR: %!s(<nil>)
--- PASS: TestPrayer (0.32s)
PASS
The duplicate was due to the GetMonthPrayer call from NewMonthPrayer, which shouldn't have been the case to begin with. It was called first but returned second, thus overwriting the existing data.
func NewMonthPrayer(input *LookupInput, prayers ...MonthPrayerIface) (*PreCurrNextMonthPrayer, error) {
var err error
t := &PreCurrNextMonthPrayer{
custData: input,
prayers: prayers,
}
t, err = t.GetMonthPrayers()
if err != nil {
return nil, err
}
return t, nil
}
I removed the NewMonthPrayer entirely as it was unnecessary, I also removed the function call in the process, thus fixing the initial problem.
// NewPrayer instantiates a prayer type object with the required input
func NewPrayer(input *LookupInput, prayers ...MonthPrayerIface) *Prayer {
return &Prayer{
custData: input,
prayers: prayers,
}
}

GRPC server does not release DB connection

I am working on a Go project which use gRPC as server and Voltdb as DB. I have an issue when sending many DB read/write requests to gRPC server. Request has been handled but it does not release DB connection after finishing operator. So when connection in pool run out I get this error:
Rejected connection from /127.0.0.1:63015 because the connection limit of 9940 has been reached
proto file
syntax = "proto3";
package grpc;
import "google/protobuf/struct.proto";
option java_multiple_files = true;
option java_package = "com.io.test";
service TestService {
rpc updateUser (UserRequest) returns (Status) {};
}
message UserRequest {
string userId = 1;
string name = 2;
}
message Status {
string code = 1;
}
Grpc implement in Go
package service
import (
"context"
"log"
api "testgrpc/pkg/api/grpc"
"testgrpc/pkg/db"
"testgrpc/pkg/model"
)
var voltDbClient *db.VoltDBClient
func init() {
voltDbClient = db.NewVoltDBClient()
}
type GrpcService struct {
}
func NewGrpcService() *GrpcService {
return &GrpcService{}
}
func (sv *GrpcService) UpdateUser(ctx context.Context, req *api.UserRequest) (*api.Status, error) {
user, err := voltDbClient.FindByID(req.GetUserId())
if user == nil || err != nil {
log.Print("error while reading")
} else {
log.Println(user.(*model.User).UserId)
log.Println(user.(*model.User).Name)
}
err = voltDbClient.Put(req.GetUserId(), req.GetName())
if err != nil {
log.Print("error while writing")
}
return &api.Status{Code: "0"}, nil
}
DB client
package db
import (
"database/sql"
"log"
"testgrpc/pkg/model"
_ "github.com/VoltDB/voltdb-client-go/voltdbclient"
)
var Db *sql.DB
func init() {
if Db == nil {
var err error
Db, err = sql.Open("voltdb", "localhost:21212")
if err != nil {
log.Fatal(err)
}
//limit max connection to reproduce issue
Db.SetMaxOpenConns(20)
}
}
type VoltDBClient struct {
}
func NewVoltDBClient() *VoltDBClient {
return &VoltDBClient{}
}
//Put add or update record
func (dbClient *VoltDBClient) Put(key interface{}, value interface{}) error {
res, err := Db.Exec("GROUPINFO.upsert", key, value)
if err != nil {
log.Println(err)
return err
}
rowCnt, err := res.RowsAffected()
if err != nil {
log.Println(err)
return err
}
log.Printf("affected = %d\n", rowCnt)
return nil
}
//FindByID find a record by ID
func (dbClient *VoltDBClient) FindByID(key interface{}) (interface{}, error) {
rows, err := Db.Query("GROUPSELECT", key)
if err != nil {
log.Println(err)
return nil, err
}
for rows.Next() {
user := model.User{UserId: "", Name: ""}
err := rows.Scan(&user.UserId, &user.Name)
if err != nil {
log.Println(err)
return nil, err
}
return &user, nil
}
return nil, nil
}
Note: I have tried run project without grpc, read/write operators work well.

Resources