BIND 10 #2404: MySQL IPv4 lease database access functions

BIND 10 Development do-not-reply at isc.org
Tue Dec 4 11:56:33 UTC 2012


#2404: MySQL IPv4 lease database access functions
-------------------------------------+-------------------------------------
            Reporter:  stephen       |                        Owner:  tomek
                Type:  task          |                       Status:
            Priority:  medium        |  reviewing
           Component:  dhcpdb        |                    Milestone:
            Keywords:                |  Sprint-DHCP-20121213
           Sensitive:  0             |                   Resolution:
         Sub-Project:  DHCP          |                 CVSS Scoring:
Estimated Difficulty:  0             |              Defect Severity:  N/A
         Total Hours:  0             |  Feature Depending on Ticket:
                                     |          Add Hours to Ticket:  0
                                     |                    Internal?:  0
-------------------------------------+-------------------------------------
Changes (by stephen):

 * owner:  stephen => tomek


Comment:

 '''src/lib/dhcpsrv/database_backends.dox'''[[BR]]
 >
 > This file should mention that MySQL is disabled by default and --with-
 dhcp-mysql is needed.
 >
 > Comment on memfile that nothing is written to external storage should
 emphasise somehow that this is a temporary limitation ...
 The file has been updated to include this information.


 '''src/lib/dhcpsrv/dhcpdb_create.mysql'''[[BR]]
 > Why are we increasing only minor version? There are incompatible changes
 (lease_time => valid_leasetime), so major version should be bumped.
 Fair point - version changed from 0.2 to 1.0 (in both dhcpdb_create.mysql
 and tests/schema_copy.h).

 In the longer term, I would like to use a utility like "dbutil" to update
 the schema.  This requires discussion with the DNS team as although the
 schema and backend database is different, there will be a lot of
 commonaility between the DNS and DHCP update programs.  I would prefer
 that we don't duplicate code.)


 '''src/lib/dhcpsrv/lease_mgr.h'''[[BR]]
 > "This is pecified" => "This is specified"
 Fixed.

 > Comment for Lease6 is truncated. There seem to be missing a line
 Fixed - appropriate line copied from Lease4.

 (Although as a note, if we were to put the definitions of getters and
 setters in the class definition, the compiler should inline them and there
 should be no penalty.  However, I think a "struct" is warranted in this
 case: I tend to use a "struct" where the intention is to collect data
 items together, and a "class" where the contained data is less important
 than the functionality the class provides.  In this case, both Lease4 and
 Lease6 are principally data stores, so a "struct" is appropriate.)

 > Comment for addr_ field in Lease6: It should be commented that it may
 contain prefix in case of IA_PD.
 Comment added.

 > Comment for iaid field: "Most containers" => "All containers"
 changed.

 '''src/lib/dhcpsrv/mysql_lease_mgr.cc'''[[BR]]
 > CLIENT_ID_MAX_LEN constant: Please comment that this is arbitrarily
 chosen value as RFC2131 does not specify upper bounds. It just follows 128
 byte limit from DHCPv6.

 > We can possibly add a comment that a minor performance boost may be
 achieved with shortening this field.
 Is that actually the case?  VARCHAR fields in the database are variable
 length and only store the data passed to them.  I'm not sure that changing
 the maximum size affects performance.

 > On a side note, it may be a good idea to mark such comments somehow. It
 will be useful when a customer comes to us and says that extra perfomance
 is needed even if corners are cut. Note that it is "when", not "if". Not
 all performance improvements are sane in general case, but it would be
 useful to have pointers to start.
 We could just ensure that such sections start "Performance:", although it
 might be better to define a suitable tag: the Doxygen command \xrefitem
 may be our friend here.  However, as adding a suitable tag such as
 "\performance" means altering the Doxygen configuration file to include a
 suitable ALIAS statement, I would prefer to get agreement with the DNS
 team first.

 > "variables n header" => "variables in header"
 Fixed.

 > "nonly to satisfy cppcheck" => "only to satisfy cppcheck"
 Fixed.

 > "structre" => "structure"
 Fixed.

 > It would be nice to add maximum field lengths in createBindForSend
 comments, e.g. hwaddr: varbinary(128).
 Done.

 > Please define const int value for bind_[] array size.
 Done.  Also added some static asserts for additional checking about array
 size limits.:w

 > "strictures" => "structures"
 Fixed

 > ADDRESS6_TEXT_MAX_LEN is 42 bytes. Comment for address:varchar states
 that we define the field as "couple bytes longer", so we can null
 terminate it. Why couple bytes and not just one? Also, is the MySQL API
 require null-termination? In that case why string length is passed as
 well?
 The documentation is not as clear as it could be, but on reflection I
 think you are correct - the number of bytes transferred from the database
 for a VARCHAR is the number of characters in the field.  As such, the
 buffer need only allocate one additional byte to allow for a trailing
 NULL.

 > Why did you remove error checking (bind_[X].error values)? There
 shouldn't be any overflows, but it is better to check it anyway. One
 example that comes to mind is overflow my happen with IDN hostnames and
 odd UTF-8 encoding. That encoding may be set by the user and internal
 MySQL conversion is outside of our control. Let's keep it.
 Mainly because it wasn't used.  But you are right, we might as well check
 for truncation now (it was a "todo").  The error variables have been
 restored and truncated data causes an exception to be thrown.

 > Why MySqlLease6Exchange::getLeaseData() does not set T1 and T2 and uses
 zeros? I haven't reviewed the tests yet, but aren't tests supposed to
 catch that?
 Should T1 and T2 be stored in the database?  At the moment, they aren't.

 > "flushed disk in the background every second or so." comment: I think
 you should remove "or so". I think (haven't checked that, so I may be
 wrong) that innodb_flush_log_at_trx_commit set to 2 means exactly 1 second
 (with the standard granularity of non-real time systems).
 Done.

 > You may add @todo here noting that innodb_flush_log_at_trx_commit
 parameter will be tweakable from bind10 configuration one day.
 Is that wise?  I'm not certain if the parameter can be altered from the C
 API and besides, it is a database tuning parameter, not really in the
 scope of BIND 10.

 > Why do we need semicolons in openDatabase() in catch() sections? Is
 there a compiler that complains about empty { } sections?
 Hangover from an old programming style.  Removed.


 > MySqlLeaseMgr::addLeaseCommon comment: Please explicitly say that this
 is for Lease4 and Lease6. Also, since this is file local class (no header
 for it), it makes sense to comment its methods using doxygen, including
 @param and @return tags.
 It is part of !MySqlLeaseManager, so the header is in the .h file.

 > getLease4: I think we should throw exception if the subnet-id field does
 not match, and not silently ignore it. This means database corruption.
 I was a puzzled when I saw this method in the !LeaseMgr class: the address
 is the unique primary key of the table so we can find at most one lease
 with that address.  The subnet_id is just an attribute of the row
 returned, so getting by address and subnet ID is really over-constraining
 the retrieval operation.

 Really, I don't think this method belongs in the lease manager: it is the
 caller that is expecting a particular relationship between address and
 subnet ID, not the database layer.  So it seems more logical for the
 caller to perform the check.  Having said that, a performance optimisation
 could be for the database to perform the search using SQL: if the
 combination of address and subnet ID is not found, it would save the
 transfer of a record from the database.

 > I think we should check errors in fields (inbind[0].error should be set
 to something and its value verified).
 The error flags are only set if the fetch returns a truncation error.
 This is now checked.

 > getName() method is supposed to return backend type. That is required
 for Kea to report which backend is used. See DHCP6_DB_BACKEND_STARTED log
 message and memfile implementation.
 getType() returns the type of backend (memfile, !MySql etc.)  getName()
 returns the name of the instance being used.  For !MySql, getType returns
 "mysql", getName returns the name of the database ("kea", "keatest" etc.)

 '''src/lib/dhcpsrv/mysql_lease_mgr.h'''
 > Major version should be increased, not minor as the changes are not
 backwards-compatible.
 Changed


 > deleteLease4/6 returns boolean value, but updateLease4/6 status is
 reported as exception (or lack of thereof). This should be consistent. I
 think exeptions are they way to go (there may be other exeptions as well,
 e.g. db error), but I'm ok with whatever consistent approach you'll
 propose.
 It's really up to you as you are writing the code that uses it.  But I
 would have thought that the cases are different:

 The desired post-condition of deleteLease() is that the lease is not in
 the database.  Whether it was there beforehand is not really relevant as
 the aim is to get rid of it.  Therefore, deleting a lease that is not
 there may be worthy of a note somewhere but it does not affect the flow of
 control of whatever is deleting the lease.  (If the pre-condition that
 presence of the lease is important, getLease should have been used to
 check.)

 The postCondition of updateLease is that a version of the lease should
 exist in the databse after the operation.  The lease not being in the
 database beforehand causes the operation to fail and the post-condition
 not to be satisfied.  This is an error.

 So in the former, status return that is option and can easily be ignored
 appears to be the way to go.  For the latter, an exception seems
 appropriate.

 > deleteLease4/6 todo comment: I'm not sure about unifying them. I think
 it would be better from the consistency perspective to keep the API
 clearly label protocol number.
 addlease does not include a numeric suffix, the version of the method
 being chosen by argumewnt type.  With deleteLease, the version of the
 method to be used can be inferred from the type of address passed to it.
 So why introduce a complication?

 > !StatementIndex enum: Comment for GET_LEASE4_CLIENTID_SUBID should be
 "// Get lease4 by Client ID and subnet ID".
 Updated.

 > addLeaseCommon(): it may not be obvious what the two flavours are.
 Adding simple "(v4 or v6)" will clarify that.
 Updated.



 > getLeaseCollection(): "any data in the collection is cleared before new
 data is added.". Why? I think it *may* be useful to obtain sum of all
 leases that meet certain criteria...
 I've removed the clear() call.  In the existing code it was unecessary
 anyway as all calls to getLeaseCollection() declare the !LeaseCollection
 object immediately before it.  The header has been updated.

 > We could query several subnets and just keep adding results to the same
 collection. I'm not sure if that is how we will implement leasequery, but
 I'm somewhat doubtful about result.clear(). If you have strong opinion
 about keeping it as it is now, I won't object.
 As noted above, as currently written the clear() was unecessary.  If we do
 go for this idea, then we will have to rewrite some of the getLease()
 methods to modify a !LeaseCollection object passed to them as opposed to
 returning one via the method return value.

 > I like the templated getLeaseCollection approach. Do you think
 specifying 3 parameter getLeaseCollection methods are needed? Templated
 getLeaseCollection could have been called directly. The only (minor)
 drawback is that you'd need to use exchange4_ and exchange6_ explicitly in
 every call. Again, that is only a suggestion. I'm fine with keeping the
 code as it is now.
 I added them more for ease of use and understandability than anything
 else.  Without them, at the call to getLeaseCollection() you would need to
 introduce the correct exchange object, which has not appeared anywhere in
 the method up to then.  With them, you don't worry about the exchange
 objects, they only appear in the common code.  The three-parameter methods
 are defined in the header, so the compiler should inline them.


 '''src/lib/dhcpsrv/tests/schema_copy.h'''
 > I think that keeping this file completely up to date with
 dhcpdb_create.mysql will be a challenging task. We will see how it goes.
 One possible approach would be to have the schema defined in one place and
 the other being
 generated out of it.
 I agree, and "But let's keep things simple for now" was my approach.  In
 the long-term I was envisioning something like the DNS "dbutil" utility
 for creating and updating the schema.  It may be possible to add something
 to that to generate the .h file.

 > Please just add a statement to dhcpdb_create.mysql that any changes to
 the schema require also schema_copy.h update. Please add is somewhere
 close to the schema_version statements.
 Done.

 '''src/lib/dhcpsrv/tests/lease_mgr_unittest.cc'''
 > Please include a one or two line comment before each test to explain the
 scope of a test. That is Shawn's idea that I very much support. We will
 convert them to a more structured test documentation one day.
 Done.

 > Lease4.Lease4Constructor: I would use much bigger values for ADDRESS.
 Currently it is 0.1.some.thing. It would be convenient to check that
 values with most significant bit set (greater than 0x7fffffff) work as
 well.
 The LeaseXConstructors are now tested with a variety of addresses.

 > Please update copyright header to 2012. !LeaseMgr is so advanced that
 nobody even dared to think about it in 2011 :)
 Done.

 '''src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc'''
 > You indented "if (name != NULL) {" line. Previous indentation was
 correct. Please revert the change.
 Done.

 > boost::shared_ptr<DUID> is used in many places. There is !DuidPtr type
 defined for it.
 Changed.

 > Also, we probably should defined !ClientIdPtr (or perhaps use !DuidPtr
 for as a !ClientId is derived from Duid class).
 I leave that up to you.

 > checkLeasesDifferent(): I agree with the first loop using ASSERT_TRUE
 instead of EXPECT_TRUE, but I question the second double loop: It will
 print out the first error and abort. It would be more informational if all
 combinations were printed...
 Done by adding a SCOPED_TRACE statement prior to the call.

 > ... Also, it should be EXPECT_EQ or ASSERT_EQ, so leases would be
 printed in case of mismatch.
 That will only work where the lease can be converted to a string.  For the
 moment, I think that just identifying which leases are equal is
 sufficient.

 > Finally, I think it would be good to compare not the shared pointers,
 but the objects they point to. This would catch the case where you have 2
 exact copies with all parameters the same.
 Good catch.  Done - of course, it did mean adding Lease4::operator==() and
 then writing unit tests for both Lease4::Operator==() and
 Lease6::operator==().


 > getLease4AddressSubnetId: Please define constants for the values used in
 tests (73, 74).
 Done.

 > getLease4ClientId, getLease4HwaddrSubnetId, getLease4Hwaddr: Please
 check that the functions do not crash when empty client-id and/or hw
 address is provided. (Remember the fun we had with zero length client-
 ids?) Please test maximum sizes of the client-id (128) and hwaddr (20).
 Done.  Also tried lengths in excess of these limits to verify they are
 truncated (although I can't find a way to get that information when the
 data is added to the database). In addition, the tests were extended to
 check DUID lengths in the Lease6 tests.

 > One generic comment about client-id: Shawn suggested that we should keep
 hardware type as the first byte in hwaddr. That is something we are doing
 in isc-dhcp. I'm not saying that we should do it within this ticket, but
 we should keep in mind that something like this will likely be
 implemented. The best course of action would be to talk about if briefly
 and the probably create a ticket for it.
 Agreed.  Or define a !HardwareAddress class which contains both type and
 value, and wrote both into separate columns in the database.

 > Generic comment about getLeaseX family of tests. They should be
 eventually refactored to cover other backends. I think we need one common
 class for all backends. As this is a huge task, we should probably create
 a separate ticket for it. I for one would like to avoid replicating this
 huge work you've done here when implementing similar tests for memfile.
 Agreed, but I don't think it's a huge task.  The tests are virtually
 completely backend agnostic, so it is really just getting them to run with
 different backends.  Gtest has "parameterised tests", which may meet that
 need.  I've created ticket #2533 for this.

-- 
Ticket URL: <http://bind10.isc.org/ticket/2404#comment:9>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development


More information about the bind10-tickets mailing list