Re: [ANN] Amazonica: Clojure client for the entire AWS api

1,361 views
Skip to first unread message

Herwig Hochleitner

unread,
Mar 25, 2013, 6:49:58 PM3/25/13
to clo...@googlegroups.com
Good one! That's just what I wanted to add cloudfront invalidation in my cms. Thanks!

I suspect, that some of the reflective support code you implemented there, might be a good fit for the java.data contrib.

James Reeves

unread,
Mar 25, 2013, 7:21:53 PM3/25/13
to clo...@googlegroups.com
It looks very interesting, but I'm afraid I really don't like "defcredential" macro. The Clojure API should really have less mutability than the Java API, not more :)

- James


On 25 March 2013 21:51, Michael Cohen <mcoh...@gmail.com> wrote:
Curious to hear opinions on this:

--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Herwig Hochleitner

unread,
Mar 25, 2013, 9:29:49 PM3/25/13
to clo...@googlegroups.com
2013/3/26 James Reeves <ja...@booleanknot.com>
It looks very interesting, but I'm afraid I really don't like "defcredential" macro. The Clojure API should really have less mutability than the Java API, not more :)

Agreed! It would be nicer to pass a context to the api functions.

The api functions could be generated such to expect an optional first context argument and otherwise expect a dynamic binding.
IMO it would also be acceptable to unconditionally take a context argument, because we have doto.

Michael Cohen

unread,
Mar 26, 2013, 2:18:17 PM3/26/13
to clo...@googlegroups.com
Thanks for the comments. I've made the suggested changes such that the AWS functions take an optional first parameter map of credentials. But I'm left wondering if it should not be required, and just do away with the stateful "defcredential" convenience. Thots?

Timothy Baldridge

unread,
Mar 26, 2013, 2:23:29 PM3/26/13
to clo...@googlegroups.com
I'm a bit concerned about all the reflection going on. Am I correct that the lib does dynamic dispatch at runtime based on both the arity and the number of arguments? The code looks like it would be painfully slow. Perhaps the hit is okay if I only want to make a dozen calls a second or so, but if I wanted more performance it seems like this library would cause me some problems. 

Besides that, I love the idea of an auto generated lib.

Timothy Baldridge


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

Michael Cohen

unread,
Mar 26, 2013, 2:33:42 PM3/26/13
to clo...@googlegroups.com
Well, of course everything is handled via reflection, which has a cost. The question is, can you afford the cost. In general, I think most of the AWS apis really aren't concerned with performance, e.g. EC2, and you're not making (many) repeated calls. If you look at something like DynamoDB though, that might be a case where you're quite concerned with performance, and potentially making numerous calls. Of course these calls are remote calls, so I'd guess that the reflection penalty is much smaller than the price you pay going out of process. As with anything though, measurement is key. I actually planned on doing some kind of benchmark comparison against Jame's excellent rotary. In general though I think for AWS reflection would not be an issue.

Hugo Duncan

unread,
Mar 26, 2013, 2:53:29 PM3/26/13
to clo...@googlegroups.com
Michael Cohen <mcoh...@gmail.com> writes:

> Well, of course everything is handled via reflection, which has a cost. The
> question is, can you afford the cost.

Or can the cost be confined to compile time...

> In general, I think most of the AWS
> apis really aren't concerned with performance, e.g. EC2, and you're not
> making (many) repeated calls.

> In general though I think for AWS reflection would not be an issue.

I think the dynamic use of reflection would be enough to put me off
using this in something like pallet, for example.

Hugo

Herwig Hochleitner

unread,
Mar 27, 2013, 10:29:00 AM3/27/13
to clo...@googlegroups.com
2013/3/26 Hugo Duncan <dunca...@gmail.com>

Or can the cost be confined to compile time...

That would be nice to have!
Generating type-hinted clojure code from the reflection result and emitting that with macros would be an option.

I think the dynamic use of reflection would be enough to put me off
using this in something like pallet, for example.

I agree with Michael on this: Any reflection overhead should pale next to the context switch and network communication, that AWS commands do.
OTOH, I also agree that driving a generated java api via reflection, to generate xml seems a bit heavy handed.
Still, first priority should be to get the interface right.

Regarding that: I think the first context argument should be mandatory.
We are just saw clojure.java.jdbc painstakenly deprecate a lot of API, to get rid of the dynamic *db* var.
The reasons against passing context in a dynamic var go double against a global atom: A function parameter can be set at from any data model in every callsite. Everything that's less flexible constrains your users for little gain (in the case of passing context).

Also, my experience with ClojureQL showed me, that with multiple sources of a context arg, it's hard to get the ordering right.
E.g. the new implementation seems to prefer dynamically bound credentials over credentials passed as argument.

Michael Cohen

unread,
Mar 29, 2013, 1:31:00 AM3/29/13
to clo...@googlegroups.com
I ran a quick and dirty benchmark comparing Amazonica with James' rotary library, which uses no explicit reflection. This was run from an EC2 instance in East, hitting a Dynamo table in the East region. tl;dr  Amazonica averaged 9ms for gets, rotary averaged 6ms, both averaged 13ms for puts. Summary is at https://github.com/mcohen01/amazonica#performance. Benchmark code is at https://github.com/mcohen01/amazonica-benchmark/blob/master/test/benchmark/runner.clj

It's pretty simplistic, but I just wanted to see if reflection just completely turned the library into a dog. Seems the contrary, that any reflection performance penalty is basically not even worth mentioning. Maybe some folks who have better understanding of jvm internals can explain if the test is invalid because of some sort of caching of the method lookups or something. 

Blake Miller

unread,
Oct 17, 2014, 7:46:44 PM10/17/14
to clo...@googlegroups.com
I know this is an old thread ... but FWIW I think this is awesome. Great work, Michael! I am going to play around with it.

Yehonathan Sharvit

unread,
Oct 21, 2014, 7:12:36 AM10/21/14
to clo...@googlegroups.com
Do you plan to have a cljs version of America

It would ne nice if you could support s3 api.

Michael Cohen

unread,
Oct 21, 2014, 1:44:00 PM10/21/14
to clo...@googlegroups.com
probably not. amazonica delegates to the official java sdk. 

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/QcGi4lPYi1s/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Greg Mitchell

unread,
Nov 20, 2014, 3:32:43 PM11/20/14
to clo...@googlegroups.com
Thanks for creating this library, Michael. Your solutions for writing the library are creative for sure, and this library has helped with developing with AWS. However, I've been using the amazonica library to communicate with AWS components in an enterprise-scale project for about a year now, and I've come to believe that some of the design choices in the library and its maintenance are big anti-patterns. All of these are things I've struggled with in developing against Amazonica:

* The documentation is sparse and the code is not self-documenting. 
Clojure in general tends to have worse and less documentation than Java. This is usually mitigated in well-designed Clojure libraries by being able to break into the source and read short, comprehensible functions with descriptive names. Amazonica is in the worst of both worlds by having no documentation in source, sparse documentation on github, and using dynamically generated code. Specific improvements to documentation I'd love to see: a comprehensive list of keys that credential maps and functions take as well as their valid values. The one or two examples per API on Github are insufficient for different combinations of functionality in real-world use cases. Pointing to AWS javadoc is not sufficient because Amazonica does name-munging and unwrapping - in order to understand the Amazonica input/output, you have to be an expert with the library and look at the implementation for name-munging. It is effectively a new API. A comprehensive list of functions would be nice, but finding them at the repl is a reasonable work around.

* Dynamically generating an API doesn't save anyone time
This is an extension of the previous point. You have almost 800 lines of code, mostly dedicated to reflection and interning methods. It's impressive that the whole thing works as well as it does, but doesn't actually save time vs. explicitly targeting an API with small wrapper functions. That has the benefit of being very obvious and easy to understand (as described above). It does mean you have to do some work when the Java SDK changes or you add a client, but I see there is already some nasty logic to switch on the client class if it has a different interface. There's a performance cost in reflection too. 

* Functions are both variadic and dispatch on argument type
Especially without clear javadoc style documentation for function arguments, types, and keys, having functions that take a smorgasborg of different arguments is incredibly confusing. I think the root of this problem is the choice to make the client methods variadic, because then there can't be well-specified arities for the various cases (no credentials or arguments, credential map, just arguments, credential map and arguments), using repl. If the functions instead had 0, 1, and 2 arities that took nothing, an argument map, or a credential map and an argument map, it would be so much clearer. Also argument maps are generally a little easier to work with than destructuring the rest.

* There are no releases or tags on github
My company has a tedious process for importing third-party packages into source control. It's not ideal, but I'm sure it's not unique. It would be great to be able to pull in a stable release built against well-specified versions of dependencies.

I hope this doesn't come across as harsh, that's not my intent. I really do appreciate you writing this library, and I realize that given how mature it is, completely changing the implementation is probably unfeasible. I just want to raise these concerns and see whether other people share them. If so, maybe they can serve as patterns or anti-patterns for future libraries.

-Greg Mitchell

John Wiseman

unread,
Nov 20, 2014, 5:00:18 PM11/20/14
to clo...@googlegroups.com
I just wanted to say that while amazonica is also my AWS library of choice, and I'm so glad it exists, and "running code wins", I also run into all the same issues that Greg listed.


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Marc Limotte

unread,
Nov 20, 2014, 5:40:35 PM11/20/14
to clo...@googlegroups.com
Good discussion, Greg.  I'll add my two cents, point by point:

> * The documentation is sparse and the code is not self-documenting. 

I've occasionally had some trouble in this area, but I actually do find that it's not too hard to map from the Java AWS api directly to clojure.  
For example, this cloudwatch request using Java API is fairly trivial to map to Amazonica API:

cwClient = new ...;
request = new ListMetricsRequest();
request.setNamespace("foo");
cwClient.listMetrics(request);

Amazonica:

(def aws-creds {...})
cw/list-metrics(aws-creds :namespace "foo")

I believe this is only possible because of the high degree of uniformity (although with a few exceptions) in the original Amazon API.

On the other hand, maybe it would be possible to auto-generate comprehensive docs using the same Reflection techniques to discover the methods in the java API.  Perhaps a clojure script that can be run to generate static doc files (Markdown or whatever).

> * Dynamically generating an API doesn't save anyone time

I suspect this is not true.  The Amazon API is huge and new services are being introduced all the time.  Hand coding wrappers to create a comprehensive API would be a huge task.  Which is probably why no one else has done it--- at best one provider supports 2 or 3 AWS services in a single API.

The comprehensive nature of the Amazonica API is it's big win-- no need to find and learn multiple libraries (one for each AWS service).

> * Functions are both variadic and dispatch on argument type
I agree with this one.  Inline keyword args often lead to trouble, where an options Map while slightly more characters to type is usually simpler.

> * There are no releases or tags on github
My company is not strict about this, so I don't experience this problem.  But I do sympathize with the request, and tags would also be helpful when trying to debug or better understand how the particular version we are using works.

marc

--

Michael Cohen

unread,
Nov 20, 2014, 7:08:42 PM11/20/14
to clo...@googlegroups.com
Hi Greg,

I think all of your criticisms are very valid, and I think I've seen
most of them voiced by others at various times. I wrote it in about 3
weeks in March of 2013, when I was very new to Clojure, so I'm sure I
made lots of mistakes. I was using some Java with AWS and some
Clojure, with James Weaver's rotary library, and when you look at the
Javadocs, it almost seems like the Java SDK could be generated. So
that leads one to think....and off I went.

As far as the problem areas you identified:

- shitty docs
What can I say, guilty as charged. Sorry. I tried to do a decent job
with the examples, although I laughed when I heard Stu Halloway slag
off "documentation by example" in some tech talk. Oops. What else
could I do? I looked at trying to generate some more codox type docs
from the Java SDK, but I just wasn't going to endure that pain. So,
yeah, I hear you. It's a trade off though. I'm sure there's a few
folks out there that would have to use Java interop and the Java SDK
(and pour through those same Javadocs) in order to use one of the less
popular AWS services if Amazonica didn't exist, because nothing else
would exist in the Clojure world for that particular AWS service. Man,
when I wrote this I don't even think there was a decent Clojure lib
for ec2.

- dynamically generated api
Yeah, the code is not as straightforward and reflection is expensive.
The counterpoint to that I guess would be, once you have it well
sorted, you don't really need to edit core.clj too much. And with most
of the AWS api, the cost of reflection is negligible compared to the
network hop. You're also probably not that concerned with latency at
all for those use cases. But again, it's a trade off. Buy one get 20
was worth a little reflection to me.

- Functions are both variadic and dispatch on argument type
Again, guilty as charged. I would do things differently today.

- There are no releases or tags on github
Again, I didn't really know what I was doing here, and never really
changed the approach. What do people expect, a snapshot release for
current development, and periodic version bumps and releases (less
frequently than Amazonica currently bumps the version numer)?

Mike
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "Clojure" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/clojure/QcGi4lPYi1s/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to

Daniel Compton

unread,
Nov 20, 2014, 9:53:16 PM11/20/14
to clo...@googlegroups.com
Regarding releases, the lein release function gives you good defaults for releasing Clojure projects, it handles tagging, commit messages, and versioning. https://github.com/technomancy/leiningen/blob/master/doc/DEPLOY.md#releasing-simplified has details. 

--
Daniel
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Gary Verhaegen

unread,
Nov 20, 2014, 9:59:17 PM11/20/14
to clo...@googlegroups.com
On Thursday, 20 November 2014, Michael Cohen <mcoh...@gmail.com> wrote:
- There are no releases or tags on github
Again, I didn't really know what I was doing here, and never really
changed the approach. What do people expect, a snapshot release for
current development, and periodic version bumps and releases (less
frequently than Amazonica currently bumps the version numer)?

No idea about the rest, but for the github part I usually expect a tag for each release on Clojars, so if I want to read the actual code of the release I'm using it is easy to find it.

This should have a very minimal impact on your workflow: it should be limited to running git tag <version number> once right after each release (or before; point is, if the unit of time is the git commit, it should be simultaneous).

Note that it is possible that you have created these tags and they are simply not on github because git does not push tags by default. You have to run git push --tags to push them.

Sam Ritchie

unread,
Nov 22, 2014, 7:06:40 PM11/22/14
to clo...@googlegroups.com
If you guys feel strongly, I'm sure the Amazonica maintainers would love a pull req that adds a small release script that handles this for them. I'm sure it'd get merged, and it'd be a nice, constructive way to move the discussion forward.

November 20, 2014 at 7:58 PM
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
November 20, 2014 at 5:08 PM
- There are no releases or tags on github
Again, I didn't really know what I was doing here, and never really
changed the approach. What do people expect, a snapshot release for
current development, and periodic version bumps and releases (less
frequently than Amazonica currently bumps the version numer)?

Mike


November 20, 2014 at 1:32 PM
Thanks for creating this library, Michael. Your solutions for writing the library are creative for sure, and this library has helped with developing with AWS. However, I've been using the amazonica library to communicate with AWS components in an enterprise-scale project for about a year now, and I've come to believe that some of the design choices in the library and its maintenance are big anti-patterns. All of these are things I've struggled with in developing against Amazonica:

* The documentation is sparse and the code is not self-documenting. 
Clojure in general tends to have worse and less documentation than Java. This is usually mitigated in well-designed Clojure libraries by being able to break into the source and read short, comprehensible functions with descriptive names. Amazonica is in the worst of both worlds by having no documentation in source, sparse documentation on github, and using dynamically generated code. Specific improvements to documentation I'd love to see: a comprehensive list of keys that credential maps and functions take as well as their valid values. The one or two examples per API on Github are insufficient for different combinations of functionality in real-world use cases. Pointing to AWS javadoc is not sufficient because Amazonica does name-munging and unwrapping - in order to understand the Amazonica input/output, you have to be an expert with the library and look at the implementation for name-munging. It is effectively a new API. A comprehensive list of functions would be nice, but finding them at the repl is a reasonable work around.

* Dynamically generating an API doesn't save anyone time
This is an extension of the previous point. You have almost 800 lines of code, mostly dedicated to reflection and interning methods. It's impressive that the whole thing works as well as it does, but doesn't actually save time vs. explicitly targeting an API with small wrapper functions. That has the benefit of being very obvious and easy to understand (as described above). It does mean you have to do some work when the Java SDK changes or you add a client, but I see there is already some nasty logic to switch on the client class if it has a different interface. There's a performance cost in reflection too. 

* Functions are both variadic and dispatch on argument type
Especially without clear javadoc style documentation for function arguments, types, and keys, having functions that take a smorgasborg of different arguments is incredibly confusing. I think the root of this problem is the choice to make the client methods variadic, because then there can't be well-specified arities for the various cases (no credentials or arguments, credential map, just arguments, credential map and arguments), using repl. If the functions instead had 0, 1, and 2 arities that took nothing, an argument map, or a credential map and an argument map, it would be so much clearer. Also argument maps are generally a little easier to work with than destructuring the rest.

* There are no releases or tags on github
My company has a tedious process for importing third-party packages into source control. It's not ideal, but I'm sure it's not unique. It would be great to be able to pull in a stable release built against well-specified versions of dependencies.

I hope this doesn't come across as harsh, that's not my intent. I really do appreciate you writing this library, and I realize that given how mature it is, completely changing the implementation is probably unfeasible. I just want to raise these concerns and see whether other people share them. If so, maybe they can serve as patterns or anti-patterns for future libraries.

-Greg Mitchell

On Monday, March 25, 2013 2:51:42 PM UTC-7, Michael Cohen wrote:
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
Sam Ritchie (@sritchie)

Daniel Compton

unread,
Nov 22, 2014, 7:12:47 PM11/22/14
to clo...@googlegroups.com
There is already a default release script included with recent versions of Leiningen. To do a release, simply run lein release :major, :minor, or :patch depending on the version bump that you want.

--
Daniel

On 23/11/2014, at 1:06 pm, Sam Ritchie <sritc...@gmail.com> wrote:

If you guys feel strongly, I'm sure the Amazonica maintainers would love a pull req that adds a small release script that handles this for them. I'm sure it'd get merged, and it'd be a nice, constructive way to move the discussion forward.

<compose-unknown-contact.jpg>
November 20, 2014 at 7:58 PM
On Thursday, 20 November 2014, Michael Cohen <mcoh...@gmail.com> wrote:

No idea about the rest, but for the github part I usually expect a tag for each release on Clojars, so if I want to read the actual code of the release I'm using it is easy to find it.

This should have a very minimal impact on your workflow: it should be limited to running git tag <version number> once right after each release (or before; point is, if the unit of time is the git commit, it should be simultaneous).

Note that it is possible that you have created these tags and they are simply not on github because git does not push tags by default. You have to run git push --tags to push them.
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
<compose-unknown-contact.jpg>
<compose-unknown-contact.jpg>
--
Reply all
Reply to author
Forward
0 new messages