Hi Ross,

On Wed, Aug 26, 2009 at 02:31:53PM -0700, Ross McFarland wrote:
> i'd heard about a new protocol, which i guess i assumed was part of a
> (new)libdrizzle.

It will be, but I wrote the new libdrizzle to replace the old one
using the same protocol for now. The new protocol will sit behind
the same interface (much like the MySQL version does today).

> > We decided providing
> > both would be the most robust (and this is fairly consistent for
> > all objects).
> 
> this one can definitely be chocked up as a difference of opinion (that i don't
> feel _that_ strongly about.) mainly i don't like the inconsistency of half of
> the time returning ret-codes and the other half pointers and taking the
> ret-codes as pass by pointer params.

Yeah, I can understand. It's not perfect, but it was either this or
more function calls to provide both APIs. I didn't really want to
get into the business of passing double pointers around, this tends
to scare more people.

> having the library do the malloc for you doesn't really seem to buy much, at
> least what it does buy you (removes a couple lines, malloc and error check) i
> don't think is worth the loss of consistency in the api.

It's not uncommon for libraries to do the memory management for
you, and like I said, I wanted to make it easy for both types of
users. Perhaps we should propose a shorter list of design questions
to the mailing list to see what folks think, this would be one of them.

> it's not just the things that aren't named for the object the really operate 
> on.
> 
> there's a lot of calls/paths that just 'feel' a little funky. the results
> objects in general fall in to this category. to me it really seems like 
> they're
> almost a query object, but not quite. i'm not sure in what situation you need 
> a
> result object that using a query object wouldn't be cleaner.

This gets down to naming of objects and the lifetime of those
objects. To me a result object was something that could last much
longer than a query object. Otherwise you have unusable fields in
a result object until the query is sent, or unusable fields in the
query object until a result is starting to be read. It seems best to
separate these objects out.

It may also be the case that a single query can produce more than one
result set (it can in MySQL, we've not added this back into Drizzle
yet). SO having a 1-1 binding of query-result would prevent this.

> other situations require calling a set of functions before an 'object' is
> usable or at least allow you to create situations are that are invalid/errors.
> creating connections is one of those (db should be a param to con_create since
> you'll always have to set_db before you connect)

I wanted to keep all _create and _destroy functions consistent
taking only the "this". I didn't want to introduce extra parameters
here. Also, it is possible to connect without any DB context, you can
just set it later in the connection or possibly reference tables via
database names as well (not sure if this is 100% supported in the
server still, but thats the plan).

> all of the multiple query stuff, which i've only glanced at, seems to have a 
> lot
> of potential for mistake and just generally (to me again) feel funky.
> 
> DRIZZLE_API
> drizzle_query_st *drizzle_query_add(drizzle_st *drizzle,
>                                     drizzle_query_st *query,
>                                     drizzle_con_st *con,
>                                     drizzle_result_st *result,
>                                     const char *query_string, size_t size,
>                                     drizzle_query_options_t options,
>                                     const void *data);
> 
> looking at that i have no clue what this function does. it takes a lot of 
> things
> and returns a query object. is that object a single query? is it a set of
> queries/what's the query added to? there's both a drizzle object and a con
> object going in, what if a drizzle object that isn't associated with the con
> object is passed? ... why is the drizzle object the first parameter, seems 
> like
> if this is a query function and i'm passing in a query object it should be the
> first param, essentially self/this.
> 
> these aren't un-grok-able problems or anything, i'm sure if you explained it 
> to
> me i would be able to make sense of and use it all.

Agreed, this is not the most inutitive, and like I said before, the
biggest thing missing here is documentation. The concurrent query
interface was something I added as a "prototype" and still needs some
refining (broader use of query objects for example). I can get into
more specific answer here, but the idea is that you can add this in the
context of a drizzle object with con being optional (in which case it
would choose a con from the pool of available cons). One of my todos
has been to refine this and clean it up. Also, right now the concurrent
query interface only supports fully buffered results, which I still
need to fix. There is a lot to do in query.c, but the basics are there.

> i don't think libdrizzle is wrong/broken, but do feel like it's
> non-obvious/sub-optimal/sometimes just doesn't feel right. i can't sit down 
> and
> list all of the things i think can be improved and frankly wouldn't have the
> energy to spell-out/fight for all of them. i'd love to work towards addressing
> them, and it is with that in mind that i started working on ldrizzle-proto. 
> that
> said others may not see problems and/or it may be too late to make those kinds
> of changes to the libdrizzle api and if that's the case i'll let it go.

This was a bit of a surprise because you've been the first person
to mention these things (which is great feedback). Others who have
used it or wrapped it have not mentioned many issues or difficulties
getting up to speed with it.

> there aren't any that are missing, that i know of, but calling them causes
> problems. if you run test in ldrizzle-proto as-is with valgrind
> --leak-check=full --show-reachable=yes --leak-resolution=high you'll see 1
> access error and a couple things being leaked.
> 
> for example if you uncomment the drizzle_free call at the end of drizzle.c
> (in my drizzle_destroy(Drizzle * drizzle) call) you'll get segfaults that
> seem to come from drizzle_free calling drizzle_con_free, which calls
> drizzle_result_free which blows up b/c all of my objects are stack
> allocated. i thought the DRIZZLE_ALLOCATED/DRIZZLE_AUTO_ALLOCATED options 
> might
> have something to do with this, but setting/unsetting them didn't seem to 
> change
> anything.

Generally, you should not be touching
DRIZZLE_ALLOCATED/DRIZZLE_AUTO_ALLOCATED. They are available for
advanced use cases, but most folks don't need them.

If you are getting access violations, it is probably because
of proper allocation order like you mention, or possibly double
freeing of objects. For example, if you free a result object that
had buffered rows in it, all those rows are automatically freed as
well, you don't need to do so manually. Same with connection objects
added to a drizzle object, you can free a con object before you free
the drizzle object, but never the other way around (drizzle_destroy
will free all connections added to it). I always check for valgrind
issues before releases and are not seeing many. For example, run
your queries through examples/client with valgrind, it should be free
(if not please let me know).

> so this seems to be the biggest problem/misunderstanding i've had. if
> drizzle_con_query isn't blocking (even without NON_BLOCKING) then that gets
> the behavior i'd imagine most cases want/need.

drizzle_con_query will block, as will all potentially blocking
functions would, without NON_BLOCKING. Not sure what you mean here.

> the memset isn't necessary, just was easier than listing out/initializing all 
> of
> the fields individually. if the desired initial state is NULLs for all 
> pointers
> and 0's for ints etc. then memset effectively is the same as listing out all 
> of
> the structs fields and setting them to NULL/0/etc. which shouldn't confuse
> valgrind. i never heard anything about memset being less effecient than 
> setting
> each of the fields individually. i would of guessed that memset would be a
> highly optimzied operation. regardless wasn't an important point...

Yes, memset usually has the same effect, but if your objects contain
fields or buffers that do not need to be zeroed out, it is less
efficient. For example, take a connection object that has a 8k buffer
in it. It is more efficient to set the individual fields than to memset
the entire thing due to the extra memory writes of the buffer. Also,
when you memset the entire buffer, you loose the possibility of
valgrind finding invalid read issues for you.

> thanks for taking the time to look my stuff over and respond. i look
> forward to continuing the discussion.

Same here! I'm very interested in working out these details to have a
simple yet powerful interface (as best we can, the two do not always
go hand-in-hand). I did a majority of this work on my own and only a
couple people have really read through and critiqued it, so I'm very
grateful for this type of feedback. I'm going to be digging back into
this type of design code in libdrizzle soon, and we're going to have
one more big backwards incompatible release soon with the new protocol,
so now is the time to refine the API.

Thanks!
-Eric

_______________________________________________
Mailing list: https://launchpad.net/~drizzle-discuss
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~drizzle-discuss
More help   : https://help.launchpad.net/ListHelp

Reply via email to