BIND 10 #374: Writable datasources: Basic support for writables in C++ datasrc library
BIND 10 Development
do-not-reply at isc.org
Wed Nov 10 13:44:02 UTC 2010
#374: Writable datasources: Basic support for writables in C++ datasrc library
------------------------------+---------------------------------------------
Reporter: jelte | Owner: UnAssigned
Type: enhancement | Status: reviewing
Priority: major | Milestone:
Component: data source | Resolution:
Keywords: | Sensitive: 0
Estimatedhours: 0.0 | Hours: 0
Billable: 1 | Totalhours: 0
Internal: 0 |
------------------------------+---------------------------------------------
Comment(by jinmei):
continuing review...still ongoing.
'''data_source.h'''
- I made some trivial editorial changes to the branch (r3500)
- you don't have to include dns/message.h. it should just suffice to
add a forward declaration of Message in the isc::dns name space.
- it's mostly the same for rrset.h, if we change ConstRRsetPtr to const
RRset& where possible as I suggested above. The only tricky part is the
return value of callbackHelper*(), but I guess it could be a bear pointer
in this context
- callbackHelperXXX(): I wonder whether it might make sense to do it
in more C++-ish way. For example, we might define the
"RRsetEnumerator" class, whose operator() returns the "next"
RRset(ptr). We also define derived classes
IteratorRRsetEnumerator, VectorRRsetEnumerator, etc, depending on
the internal container. Then doIXFR() would look like:
{{{
DataSrc::WriteResult
DataSrc::doIXFR(DataSrcTransaction& transaction,
RRsetEnumerator& enumerator)
{
...
ConstRRsetPtr final_soa = enumerator();
if (!final_soa) {
return (W_ERROR);
}
ConstRRsetPtr first_soa = enumerator();
if (!first_soa) {
return (W_ERROR);
}
...
}
}}}
This is not only about a matter of style, but also about more
practical advantages. That is, this way we can make it type safer
by not relying on passing around void* values and cast them to
particular type of values. Also, with this approach we can hide
derived classes like "VectorRRsetEnumerator" in the test code (it
seems to me this one is introduced for the convenience of tests; in
practice we'd normally if not always use RRsetIterator).
Alternatively we could make doIXFR(), etc templated so that it can
take generic iterators. It might be more C++-ish, although we
couldn't do that in this current design because doIXFR() is a
virtual function.
In either case, for a vector based interface I'd use the begin and
end iterators instead of an index.
- AbstractDataSrc::WriteResult I'd make the comment for each enum
value a doxygen description (using '//<')
- DataSrcTransaction
- I'm not sure if it makes sense to attribute "zone name/class" to
this class, because a transaction in a data source may not always
be specific to a single zone. for example, we may want to add
more than one zones to a data source as an atomic transaction.
Also, even for the case where we add a single zone (which is not
possible through the current public interface though), since the
zone information is an initial attribute of DataSrcTransaction, it
would look like
{{{
data_source.addZone(trans);
}}}
this is a bit awkward in terms of interface consistency because
when we add an RRset we pass the RRset(ptr) to be added.
- so, I wonder whether we might make this class is a smaller
primitive: just holding a scoped transaction for a data source,
similar to the Boost scoped_lock. Then the code would look like
as follows:
{{{
void doIXFROrSomething(data_source) {
DataSrcTransaction trans(data_source);
//
// do whatever necessary within the transaction
//
trans.commit();
}
}}}
Below, however, I'll continue the review assuming the current
interface for now.
- member variable names shouldn't begin with "_" (which is (maybe by
convention) reserved for standard definitions, in my understanding)
- it may be better to provide doxygen description for states? and,
according to the guideline, "states" should probably better be
"States".
- constructor: 'explicit' is not necessary for multi-parameter
constructor.
- destructor: since the behavior of the destructor may be critical
(see the comment to its implementation), I think we should add
some note in the doxygen style.
- the getters should better be member const functions.
- 'const' to the returned object type is redundant (getZoneName()
and getZoneClass())
- I'd define _zone_name and _zone_class as const.
- I'm not sure why we pass a pointer of data source. can it be
NULL?
- getZoneName() could return a reference to _zone_name, which would
be a bit more efficient.
- I'd feel nervous about the setters (setState() and setData()).
Due to them _state and _data are effectively public member
variables, and make the class quite fragile to careless (buggy)
usage. for example, a buggy implementation could set the state
from "RUNNING" to "INIT" (or to "DONE" without commit or
rollback), which would cause something very weird. Likewise, an
application may modify or even reset to NULL the internal data.
Since the Sqlite3DataSrc implementation assumes it's always
a valid pointer containing expected data, it would make the
application crash. We could blame the application saying it's
"their bug", but it would be much better to prohibit such risky
usage via the interface.
- A simpler "scoped transaction" would be one possibility. Another
is to introduce a class hierarchy of transactions from a base
abstract class to a specific derived class per data source. With
this approach, we define e.g. Sqlite3DataSrcTransaction derived
from base DataSrcTransaction and have an instance of
Sqlite3DataSrc create an associated transaction:
{{{
DataSrcTransaction* trans = data_source.createTransaction();
// here, if data_source is of Sqlite3DataSrc, trans is of
// Sqlite3DataSrcTransaction.
data_source.addRRet(*trans, rrset); // or something like this
}}}
Then, we can hide the interface and implementation within the
definition
of Sqlite3DataSrcTransaction.
- getDataSource() doesn't seem to be used. since it's a mutable
"handle", it would be better to not to disclose this information
until we see the real need for it, that is, I suggest removing this
method for now.
- DataSrc class: the above design-level comment would also affect the
modifications to DataSrc, but here I'll review it as is.
- in general, I'd like to know whether/when the methods throw an
exception.
- addRRset(): '\return' is missing, in which W_SUCCESS should be.
I'd be interested in what happens if the same <name,type> of rrset
already
exists.
- delRRset(): \return is missing. what if no such RRset exists?
- delZone(): there should be a period at the end of \brief line.
otherwise doxygen considers the next line a part of the brief
description. what if no such zone exists?
(to be continued)
--
Ticket URL: <http://bind10.isc.org/ticket/374#comment:5>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development
More information about the bind10-tickets
mailing list