Introducing memcache-ada, a memcached client in Ada

14 views
Skip to first unread message

R Tyler Croy

unread,
Dec 19, 2010, 7:43:06 PM12/19/10
to

My first "real" project in Ada has been a memcached client written in Ada:
<https://github.com/rtyler/memcache-ada>

Right now it has basic functionality of being able to "get" a key, "set" a
value, "delete" a value and increment/decrement values.

There's still a few functions unimplemented, but I think it's in a usable state
right now.

I look forward to any comments or suggestions as to the code
quality/structure.


Cheers

--
- R. Tyler Croy
--------------------------------------
Code: http://github.com/rtyler
Chatter: http://twitter.com/agentdero
http://identi.ca/dero

Thomas Løcke

unread,
Dec 20, 2010, 3:25:58 AM12/20/10
to
On 2010-12-20 01:43, R Tyler Croy wrote:
>
> My first "real" project in Ada has been a memcached client written in Ada:
> <https://github.com/rtyler/memcache-ada>
>
> Right now it has basic functionality of being able to "get" a key, "set" a
> value, "delete" a value and increment/decrement values.
>
> There's still a few functions unimplemented, but I think it's in a usable state
> right now.
>
> I look forward to any comments or suggestions as to the code
> quality/structure.


Hey,

It looks pretty good to me.

I do though have four suggestions:

1. Declare all the string literals ("STORED", "NOT_FOUND" and such) as
constants in the specification.

2. Get rid of the ASCII.x and use Ada.Characters.Latin_1 instead. The
ASCII package is considered obsolescent. Oh, and perhaps declare the
ASCII.CR & ASCII.LF combo as a CRLF constant also?

3. Take a look at the various search subprograms in Ada.Strings.Fixed
and Ada.Strings.Unbounded. Some of them might be able to replace a loop
here or there.

4. Place your subprograms in alphabetical order. Actually, that might
just be me, but it hurts my eyes seeing Append_CRLF down there at the
bottom. :D

I can't comment on the actual functionality of the program, as I've
never used memcached, but at a quick glance everything looks very neat
and tidy.

As far as I can tell from the repo, you've only been at this since early
November. If this is your first ever Ada project, then I'd say you're a
pretty damn fast learner! I'm only a little jealous.

--
Regards,
Thomas Lųcke

Email: tl at ada-dk.org
Web: http:ada-dk.org
IRC nick: ThomasLocke

Georg Bauhaus

unread,
Dec 20, 2010, 5:16:05 AM12/20/10
to
On 20/12/2010 09:25, Thomas Lųcke wrote:
> I do though have four suggestions:
>
> 1. Declare all the string literals ("STORED", "NOT_FOUND" and such) as
> constants in the specification.

Not sure why should they be public? These are protocol words
that I think a user will not want to deal with.

> 3. Take a look at the various search subprograms in Ada.Strings.Fixed
> and Ada.Strings.Unbounded. Some of them might be able to replace a loop
> here or there.

Key string scanning looks just right as is to me where,
e.g., Is_Control is used, when verbiage will otherwise
be needed to pack every pattern into one object suitable
for Index.

You could say

Key_Ch = ' '
in place of
Character'Pos (Key_Ch) = 32

or use the white space characterising functions (like you
used is_control), or exclude everything with a lower 'Pos
if memcached wants this.

I'd add an occasional "constant" to the String objects'
declarations in the declare blocks. Compiler option
-gnatwa warns about this and other issues.

One more general comment, addressed to all interested:
Don't we have a portable POSIX socket library? As much as I'm
fond of GNAT, placing GNAT's helpful, yet compiler specific
libraries in every program will make them non-Ada (in a
portability sense). In the case of sockets, I don't see a
compelling need to be compiler specific?

Thomas Løcke

unread,
Dec 20, 2010, 4:36:11 AM12/20/10
to
On 2010-12-20 11:16, Georg Bauhaus wrote:

> On 20/12/2010 09:25, Thomas Lřcke wrote:
>> I do though have four suggestions:
>>
>> 1. Declare all the string literals ("STORED", "NOT_FOUND" and such) as
>> constants in the specification.
>
> Not sure why should they be public? These are protocol words
> that I think a user will not want to deal with.


Do they have to be made public in order to be declared as constants in
the specification? Couldn't they be declared in the private part of the
package?

I'd prefer declaring this once:

Not_Found : constant String := "NOT_FOUND";

And then using Not_Found throughout the package, instead of having
multiple "NOT_FOUND" literals.

OTOH I do see your point about visibility. :o)

--
Regards,
Thomas Lřcke

Jeffrey Carter

unread,
Dec 20, 2010, 2:36:45 PM12/20/10
to
On 2010-12-20 01:43, R Tyler Croy wrote:
>
> I look forward to any comments or suggestions as to the code
> quality/structure.

I have looked only at the specification, since that's the only part a client
should need to look at. Initial impressions:

I see a number of declarations in the visible part of the specification that are
not referenced in the visible part nor needed for the client to use the package.
Only things needed in the visible part or needed by the client to use the
package should appear in the visible part.

[In order of preference, things should be declared

1. In the body
2. In the private part (Is Ada the only language with private parts?)
3. In the visible part]

Similarly, context clauses should use "private with" for things only referenced
in the private part.

The type Flags and the Set_Flags parameters of that type are not documented in
the spec; if possible, they should be. In particular, the effect of the default
value for Set_Flags should be described.

The meaning of the expiration parameters, and especially of their default
values, should be documented. The default of zero would seem to mean immediate
expirations, which doesn't seem very useful.

The meaning of the Boolean return values from some of the functions (which also
appear as parameters) is unclear and should be documented.

Some exceptions appear at the end of the visible part. Presumably some of the
operations may raise these exceptions, but which operations raise which
exceptions, and in what circumstances, is not documented. Since declarations
should appear before they are referenced, the exception declarations should
appear before the comments in which they are referenced.

--
Jeff Carter
"Crucifixion's a doddle."
Monty Python's Life of Brian
82

R Tyler Croy

unread,
Dec 20, 2010, 3:14:12 PM12/20/10
to
On 2010-12-20, Georg Bauhaus <rm.tsoh.plus...@maps.futureapps.de> wrote:

I was thinking about this last night to be honest, I felt somewhat frustrated
with all the GNAT-specific calls I had in the code base. There is the
adasockets library <https://github.com/samueltardieu/adasockets> but when I was
starting out I was a little apprehensive abvout including a third-party
dependency (I don't know what other compilers somebody might want to use).


Not sure what the best solution is, but I can relate to your feelings towards
compiler-specific functionality.

R Tyler Croy

unread,
Dec 20, 2010, 3:16:11 PM12/20/10
to

These are some really good suggestions, thank you for taking the time to look
over the protocol and my code

I look forward to updating some things tonight after work. :)

Simon Wright

unread,
Dec 20, 2010, 5:03:13 PM12/20/10
to
Jeffrey Carter <spam.jrc...@spam.not.acm.org> writes:

> The type Flags and the Set_Flags parameters of that type are not
> documented in the spec; if possible, they should be. In particular,
> the effect of the default value for Set_Flags should be described.
>
> The meaning of the expiration parameters, and especially of their
> default values, should be documented. The default of zero would seem
> to mean immediate expirations, which doesn't seem very useful.
>
> The meaning of the Boolean return values from some of the functions
> (which also appear as parameters) is unclear and should be documented.

I think these are "symptoms" of a thin (thinnish) binding. In
particular, the type Expiration and its use is rather non-Ada style. Is
it really the case that the memcached server won't allow you to specify
a retention period greater than 30 days? (just checked -- yes -- well,
the Google Python API says "up to 1 month", close enough I suppose :-).

I don't see why Set appears as two functions & one procedure? Would it
be sensible to say, perhaps,

procedure Set (....); -- no expiry specified

procedure Set (....;
Expiring_After : Duration);

procedure Set (....;
Expiring_At : Ada.Calendar.Time);


Why is type Connection tagged? I guess you have some expansion plans for
it?!

R Tyler Croy

unread,
Dec 20, 2010, 5:48:01 PM12/20/10
to

I was concerned about "hiding" too much of how Memcached works from the user by
using "Duration" instead of the Expiration subtype that I created.

As for the multiple overloads of Set/Get, that's to address a problem I'm not
sure has a good solution. With Memcached, and I'd assume most other
networked-services like it, there are expected error conditions where I
personally find exceptions to be unnecessary.

To account for this, I wanted to define the function which return booleans
(perhaps a "Success", "Failure" enum would be better) in case the caller
"cared" about the success of a Set call. While still providing a version of the
same code which returns nothing, in the case of the user "not caring" what the
Set returns as long as it doesn't except (Invalid_Key_Error for example)
>

> Why is type Connection tagged? I guess you have some expansion plans for
> it?!

Because I don't know why because, I was under the impression that tagging it
was the only way to get "Connection.Get ()" style invocations under Ada 2005

Simon Wright

unread,
Dec 20, 2010, 6:44:09 PM12/20/10
to
R Tyler Croy <ty...@linux.com> writes:

> On 2010-12-20, Simon Wright <si...@pushface.org> wrote:

>> I think these are "symptoms" of a thin (thinnish) binding. In
>> particular, the type Expiration and its use is rather non-Ada style. Is
>> it really the case that the memcached server won't allow you to specify
>> a retention period greater than 30 days? (just checked -- yes -- well,
>> the Google Python API says "up to 1 month", close enough I suppose :-).
>>
>> I don't see why Set appears as two functions & one procedure? Would it
>> be sensible to say, perhaps,
>>
>> procedure Set (....); -- no expiry specified
>>
>> procedure Set (....;
>> Expiring_After : Duration);
>>
>> procedure Set (....;
>> Expiring_At : Ada.Calendar.Time);
>
> I was concerned about "hiding" too much of how Memcached works from
> the user by using "Duration" instead of the Expiration subtype that I
> created.

Not bad reasons. But - for thicker bindings - you do end up hiding the
gory details.

For instance, you could implement Expiring_After > 30 days by
calculating the absolute time & passing that instead .. in fact, why not
do that always? (that's how GNAT implements 'delay').

> As for the multiple overloads of Set/Get, that's to address a problem
> I'm not sure has a good solution. With Memcached, and I'd assume most
> other networked-services like it, there are expected error conditions
> where I personally find exceptions to be unnecessary.
>
> To account for this, I wanted to define the function which return
> booleans (perhaps a "Success", "Failure" enum would be better) in case
> the caller "cared" about the success of a Set call. While still
> providing a version of the same code which returns nothing, in the
> case of the user "not caring" what the Set returns as long as it
> doesn't except (Invalid_Key_Error for example)

Pretty good reasons. An alternative might be an 'out' parameter.

>> Why is type Connection tagged? I guess you have some expansion plans
>> for it?!
>
> Because I don't know why because, I was under the impression that
> tagging it was the only way to get "Connection.Get ()" style
> invocations under Ada 2005

Yes, that's right. I've not got used to "Connection.Get ()" yet,
explains why you called the leading parameter This! (I was wondering
whether to mention it; my personal style would probably have been to
name it C for Connection; or maybe To, & From for Get).

Randy Brukardt

unread,
Dec 20, 2010, 7:56:17 PM12/20/10
to
"Georg Bauhaus" <rm.tsoh.plus...@maps.futureapps.de> wrote in
message news:4d0f1ed7$0$6990$9b4e...@newsspool4.arcor-online.net...
...

> One more general comment, addressed to all interested:
> Don't we have a portable POSIX socket library? As much as I'm
> fond of GNAT, placing GNAT's helpful, yet compiler specific
> libraries in every program will make them non-Ada (in a
> portability sense). In the case of sockets, I don't see a
> compelling need to be compiler specific?

There isn't. We had a project to make a compiler-independent socket library
as part of Ada 2005. But no one took the point on it, and I simply was
spread too thin to want to take on another major project.

We've talked about splitting "Claw.Sockets" out of Claw, since it doesn't
depend much on Claw. (The origin of Ada.Directories was Claw.Directories,
which also didn't depend much on Claw.) Tom Moran made an NC-Sockets ("NC"
standing for "Not Claw"), but it'll have to be ported to Linux (which I
don't expect to be hard) in order to be generally useful.

Claw.Sockets is mostly stream-oriented; it doesn't provide all of the
functionality of the raw sockets level in order to make an abstraction much
more like the file abstractions that Ada programmers are familiar with. In
that sense, it is a bit higher level than the GNAT sockets and Adasockets.
That seems better to me, presuming that you don't need the low-level
functionality [not sure why you would, but I am never surprised anymore
about that sort of thing].

Randy.


Dmitry A. Kazakov

unread,
Dec 21, 2010, 2:52:07 AM12/21/10
to
On Mon, 20 Dec 2010 18:56:17 -0600, Randy Brukardt wrote:

> Claw.Sockets is mostly stream-oriented; it doesn't provide all of the
> functionality of the raw sockets level in order to make an abstraction much
> more like the file abstractions that Ada programmers are familiar with.

BTW raw sockets, they don't work anymore under Windows since XP SP 3 (or
2). In order to have them working an Ada implementation would need to
install an NDIS driver.

> In
> that sense, it is a bit higher level than the GNAT sockets and Adasockets.
> That seems better to me, presuming that you don't need the low-level
> functionality [not sure why you would, but I am never surprised anymore
> about that sort of thing].

I don't consider raw sockets low-level, they are in OSI sense, but from the
software design point of view it is just a different I/O interface.

If I designed a socket library for Ada, I would separate all kind of
interfaces (TCP, UDP, raw, PGM etc) into different child packages.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

Georg Bauhaus

unread,
Dec 21, 2010, 6:10:21 AM12/21/10
to
On 21.12.10 00:44, Simon Wright wrote:

> For instance, you could implement Expiring_After > 30 days by
> calculating the absolute time & passing that instead .. in fact, why not
> do that always? (that's how GNAT implements 'delay').

Passing the absolute times with cached objects? For illustration,
the way Google's cloud seems to handle points in time renders this
adventurous, I think. A Google App Engine program will typically
use memcached. Depending on the node executing the parts
of your software, "absolute" times may be shifted by
several hours in some direction. I haven't found a rule yet
by which I could predict times. E.g., it is 12-17 06:04AM 54.659
Google time now, my wall clock showing 12:04AM for this.
I understand Google App Engine programs can have parts moved
between nodes.

In a setting as volatile as this, a "time span" associated with
a cached object seems preferable.

Georg Bauhaus

unread,
Dec 21, 2010, 6:21:16 AM12/21/10
to
On 21.12.10 12:10, Georg Bauhaus wrote:
> Depending on the node executing the parts
> of your software, "absolute" times may be shifted by
> several hours in some direction. I haven't found a rule yet
> by which I could predict times. E.g., it is 12-17 06:04AM 54.659
> Google time now, my wall clock showing 12:04AM for this.
> I understand Google App Engine programs can have parts moved
> between nodes.

If Unix Epoch time is the same on all Google nodes,
there shouldn't be a problem with passing times, I guess.

Reply all
Reply to author
Forward
0 new messages