[go-nuts] db interface design

2018-05-20 Thread yang sheng
Hi

I am trying to practice golang and have some questions regarding to 
interface design.

Say I want to create a simple app to store the book and its author info. I 
am planning to create interfaces so that I can use different db driver 
(mysql/mongo/redis...) as long as if they implemented DB interface:
https://play.golang.org/p/DOw83_OqBOe

I have a couple questions:

1. there are a lot of similar functions, for example NewBook and NewAuthor. 
The difference is type of argument passed into the function. Is it a good 
idea to combine those 2 functions with a generic New(interface{}) (string, 
error) function and  reflect the actual type inside? 

2. Sometimes we query based on one or more conditions (where clause),  for 
example find books based on author/release data/ price.  Should I create a 
generic function like:

func FindBooks(map[string]interface{}) ([]Book, error)

that passed in a map of condition kv and do the typer assertion of each 
condition key values?


Thanks for any advice


-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [go-nuts] db interface design

2018-05-21 Thread yang sheng


On Monday, May 21, 2018 at 3:47:59 AM UTC-4, Sergey Kamardin wrote:
>
> > 1. there are a lot of similar functions, for example NewBook and 
> NewAuthor. 
> > The difference is type of argument passed into the function. Is it a 
> good 
> > idea to combine those 2 functions with a generic New(interface{}) 
> (string, 
> > error) function and  reflect the actual type inside? 
>
> I would not recommend to do this. Eventually logic of insertion may 
> become much different and your generic function's code will become a 
> very hard to read. Also, there is a little bit overhead on using 
> interfaces for non-pointer structs like `Book` and so on. 
>

Yes I agree. It might get messy in future. So I guess creating insertion 
function for each struct type might make sense here.

>
> > 2. Sometimes we query based on one or more conditions (where clause), 
>  for 
> > example find books based on author/release data/ price.  Should I create 
> a 
> > generic function like: 
> > func FindBooks(map[string]interface{}) ([]Book, error) 
>
> It can be done in this way, but I could suggest to use some struct 
> `BookCriteria` with non-interface fields, say `Author string` and so on. 
> Then, your `FindBooks` implementation may accept that struct instance.   
>
> Or, for example, `FindBooks` may accept variadic functional options that 
> can fill appropriate fields of criteria. For more about functional 
> options you could find in Dave Cheney's article: 
>
> https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis 
>

I didn't thought of functional option previously. Thanks for the hint. I 
will try to refactor the code base. 

Thanks! 

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[go-nuts] choose buffered channel or unbuffered?

2018-02-12 Thread yang sheng


I am trying to design a small app to send emails (stored in MongoDB) using 
golang utilizing goroutine and channel and this is the first time i use 
golang.


There are 2 types of worker in each server (Retriever and Sender). A single 
retriever will periodically (every few seconds) query MongoDB, fetch new 
email. Then new email will be sent to Senders via a buffered channel (has 
buffer size == number of Sender workers, this might be the issue). When a 
sigterm received, Retriever will exist using ctx.Done and close the email 
channel.  Since channel closed, Senders which range over channel will exit 
as well.


go func() {
defer wg.Done()
for {
select {
case <-ctx.Done():
// received sigterm, close channel
close(emailCh)
return
case <- time.After(5 * time.Second):
// query DB every 5 seconds to get a single new email
if newEmail := dw.getNewEmail(); newEmail != nil {
emailCh <- newEmail
}
 }
}



I realize this is a bad design and the issues are:

 I am using FindAndModify to fetch email every time ( I need to update 
email status to sending when fetching so that the app running in other 
server will not fetch the same doc). That means I am actually sending a 
single email in each interval which makes it non-sense to create multiple 
Sender goroutines. 


the alternative I can think of right now is:

1. I could run FindAndModify query multiple times in each interval to get N 
emails. But If the N is larger then buffer size, it will block the select 
and blocking time depends on how many more emails I retrieved than the 
buffer size. If N is too large, then sigterm received, I am not be able to 
shutdown the app in time. If N is too small, I can only send at most 
N/interval emails per second.


2. use an unbuffered channel without interval, (keeps polling the DB). 
Whenever there is a new email, send to channel. the blocking time should be 
minimal (1 email). But the app will continually poll the DB, will it create 
big overhead/too many connections?



go playground link:

https://play.golang.org/p/LRQ-cnLiDfB

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.