Thursday, March 3, 2016

Don’t call your state ‘state’

In the OO world you are frowned upon if you call something object. Its time to extend this principle: don’t call your state state. This post is about why this is a bad idea and what you can do about it.

Recently we sat down to discuss the new data model for our messaging system at eBay’s Classifieds Group. One of the things we inherited from the past is the entity called conversation with a field called state. Possible values were Ok, On hold and Blocked.

So what was the problem?
A field called ‘state’ almost always has a very intuitive meaning. Unfortunately, the word is so vague that the meaning can easily warp, depending on the problem at hand. I noticed this in a couple of projects: the state field started to collect more and more possible values. With more values came increasingly difficult state transitions. This lead to code that was way more messy then necessary.

For example, in our conversation entity we could introduce the state Closed to indicate that a participant wants to stop the conversation. Then we continue by adding the state Archived to indicate that the conversation should be hidden until a new messages arrive.

What can we do?
The key observation is that each state value represents multiple behaviors. Think about it, what behavior is needed in each state? How do these behaviors change for each state? These questions will lead you to multiple fields that can represent the entire state of your entities.

Within a couple of minutes we found three behaviors we wanted to have for our conversations: a conversation is either visible or not (field visibility with values Displayed and Hidden), it will accept new messages or not (field acceptNew with values Accept and Reject) and we want to notify the recipient of a new message (or not) (field notifyOnNew with values Notify and Mute).Not only did our code become easier to extend and reason about, as a bonus we found a feature that would have been really hard with the old model: muting a conversation.

Conclusion
Don’t call your state ‘state’, instead, think about the behavior each state represents and model that instead.

Sunday, February 14, 2016

Generate a certificate signing request (CSR) as a 1 liner

As I run my own (secure) web and mail server I frequently have to get certificates. You could run with a selfsigned certificate, but that is not ideal; desktop browsers are very noisy about them, and mobile browsers are outright hostile. Installing certificates on a mobile phone are no fun (though, hat of for CAdroid).

Now that Letsencrypt is live, I wanted to try it out. However, Letsencrypt does not make it easy for you to keep using the same private key. This is necessary as the Android's http client pins your certificates (the one I needed anyway). Luckily Letsencrypt allows you to use a CSR that is generated outside of their tools.

Letsencrypt certificates are only valid for a short time (90 days) so automation is key. Unfortunately, openssl does not make it easy to fully automate creating CSRs, especially when you need 'Alternative Names', a requirement from Letsencrypt.

Luckily I found a solution from Andrew Leahy. Here it is:

openssl req -new -nodes -sha256 -key private-key.pem \ -subj "/C=US/ST=CA/O=Acme, Inc./OU=Acme Example/CN=example.com" \ -reqexts SAN \ -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) \ -out domain.csr

This will generate a CSR in file domain.csr with set country (C), state (ST), organization (O), organization unit (OU) and most importantly the common name (CN) and in addition two alternative names example.com and www.example.com. Thanks Andrew!

Wednesday, May 20, 2015

5 problems in an hour

@svpino posted a nice chalenge: solve 5 problems within an hour or denounce your title of software engineer.

It took me 40 minutes, of which 10 minutes was fighting around a limitation of the scala REPL.

Here are my solutions. They can all be pasted as is in the Scala REPL.

///////////////////////////////////////// // Problem 1 (Yuc! This is not a Scala problem!) def p1_for(xs: Seq[Int]): Int = { var s = 0; for (x <- xs) s += x; s } def p1_while(xs: Seq[Int]): Int = { var s = 0; var i = 0; while (i < xs.length) {s += xs(i); i+=1}; s } def p1_rec(xs: Seq[Int]): Int = if (xs.isEmpty) 0 else xs.head + p1_rec(xs.tail) def p1_proper(xs: Seq[Int]): Int = xs.foldLeft(0)(_+_) // :) scala> p1_for(Seq(1,2,3)) res0: Int = 6 scala> p1_while(Seq(1,2,3)) res1: Int = 6 scala> p1_rec(Seq(1,2,3)) res2: Int = 6 ///////////////////////////////////////// // Problem 2 def p2[A,B](a: Seq[A], b: Seq[B]): Seq[Any] = a.zip(b).flatMap { case (a,b) => Seq(a,b) } scala> p2(Seq("a","b","c"), Seq(1,2,3)) res3: Seq[Any] = List(a, 1, b, 2, c, 3) ///////////////////////////////////////// // Problem 3 def fibonaci: Iterator[Int] = Iterator.iterate((0,1)) { case (a,b) => (b, a+b) }.map(_._1) scala> fibonaci.take(100).mkString(", ") res79: String = 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 55... ///////////////////////////////////////// // Problem 4 def p4(n: Seq[Int]): Int = n.map(_.toString).sorted.reverse.mkString("").toInt scala> p4(Seq(50,2,1,9)) res73: Int = 95021 ///////////////////////////////////////// // Problem 5 sealed trait Expr { def evaluate: Int def show: String } case class Plus(e1: Expr, e2: Expr) extends Expr { def evaluate: Int = e1.evaluate + e2.evaluate def show: String = e1.show + " + " + e2.show } case class Min(e1: Expr, e2: Expr) extends Expr { def evaluate: Int = e1.evaluate - e2.evaluate def show: String = e1.show + " - " + e2.show } case class Literal(v: Int) extends Expr { def evaluate: Int = v def show: String = v.toString } def appendToLastLiteral(e: Expr, append: Int): Expr = e match { case Plus(e1, e2) => Plus(e1, appendToLastLiteral(e2, append)) case Min(e1, e2) => Min(e1, appendToLastLiteral(e2, append)) case Literal(v) => Literal((v.toString + append.toString).toInt) } def loop(e: Expr, todo: Seq[Int]): Unit = { if (todo.isEmpty) { if (e.evaluate == 100) println(e.show) } else { loop(Plus(e, Literal(todo.head)), todo.tail) loop(Min(e, Literal(todo.head)), todo.tail) loop(appendToLastLiteral(e, todo.head), todo.tail) } } scala> loop(Literal(1), Seq(2, 3, 4, 5, 6, 7, 8, 9)) 1 + 2 + 3 - 4 + 5 + 6 + 78 + 9 1 + 2 + 34 - 5 + 67 - 8 + 9 1 + 23 - 4 + 5 + 6 + 78 - 9 1 + 23 - 4 + 56 + 7 + 8 + 9 12 + 3 + 4 + 5 - 6 - 7 + 89 12 + 3 - 4 + 5 + 67 + 8 + 9 12 - 3 - 4 + 5 - 6 + 7 + 89 123 + 4 - 5 + 67 - 89 123 + 45 - 67 + 8 - 9 123 - 4 - 5 - 6 - 7 + 8 - 9 123 - 45 - 67 + 89

Thanks @svpino, this was fun!

Friday, November 1, 2013

Installing gnutar on Maverick

Unfortunately Apple decided to remove /usr/bin/gnutar from Maverick (Mac OSX 10.9). This is a pain because most of the tarring I do on my mac is to transfer the file to a GNU based linux (e.g. Debian/Ubuntu). Apple's bsd-tar is not compatible with gnu-tar.

This is my solution:

brew install gnu-tar cd /usr/bin sudo ln -s /usr/local/opt/gnu-tar/libexec/gnubin/tar gnutar

Wednesday, September 18, 2013

Configuring Postfix/Dovecot for Microsoft Windows Live Mail

Personal mail gets no love from Microsoft. The last 10 year I have not seen their product change a lot. Notable I see name changes (always a bad sign) and some visual changes. The actual implementation is still the same: not respecting standards. I run a Postfix/Dovecot installation for my family mail. I have had many many different email clients connect to it without large problems. With Microsoft Windows Live Mail, Outlook Express or whatever it is called today, it just doesn't work. Anyway, here is what you can do:

(I am assuming you are using something like Ubuntu with Postfix for SMTP with TLS (actually STARTTLS) on port 25, and Dovecot with IMAPS on port 993.)

Open the file /etc/dovecot/dovecot.conf, and update the line with auth_mechanisms to the following. The trick is that login has to come first:

auth_mechanisms = login plain

Repeat this trick for Postfix in /etc/postfix/sasl/smtp.conf:

mech_list: login plain

Restart Postfix and Dovecot, and you're good to go!

Monday, July 1, 2013

Installing thrift through homebrew

Currently thrift support in homebrew is broken; brew versions thrift returns nothing. Here is a list I got from a collegae:

$ brew versions thrift 0.9.0 git checkout 3b8bb74 /usr/local/Library/Formula/thrift.rb 0.8.0 git checkout e5475d9 /usr/local/Library/Formula/thrift.rb 0.7.0 git checkout 141ddb6 /usr/local/Library/Formula/thrift.rb 0.6.1 git checkout 54ff633 /usr/local/Library/Formula/thrift.rb 0.5.0 git checkout 0476235 /usr/local/Library/Formula/thrift.rb 0.4.0 git checkout 4523877 /usr/local/Library/Formula/thrift.rb 0.3.0 git checkout 67ec3c0 /usr/local/Library/Formula/thrift.rb 0.2.0 git checkout d0efd9e /usr/local/Library/Formula/thrift.rb HEAD git checkout c4decd7 /usr/local/Library/Formula/thrift.rb

With this information in mind, continue to stackoverflow for more information on how to install a specific version.

I had to do brew -v install thrift (with -v), otherwise installation just halted.

Sunday, June 30, 2013

Announcing Metrics-Scala 3.0.0

Metrics-scala 3.0.0 was just released and is available in Maven central. This is the first release against Metrics version 3.0.0, and the first release where the code is maintained by me instead of being a line for line copy of Coda Hales' original.

A special thanks goes to @scullxbones who started the 3.0.0 branch and ported the tests to ScalaTest.

Changes:

  • Code is no longer a copy from Coda Hale's sources and is now maintained by me.
  • Depends on Metrics-core 3.0.0.
  • Ported tests from original to ScalaTest (@scullxbones).
  • Much more documentation.

Although the metrics-scala API is mostly source compatible, there are breaking API changes which are mostly caused by changes in the metrics-core library:

  • All code moved to the nl.grons.metrics.scala package (changed at Coda Hale's request).
  • The class Instrumented must now be created in your project by extending InstrumentedBuilder.
  • All configuration for histograms, meters, and timers are gone. These are now configured in the reporter.
  • Dropped method clear on Histogram and Timer.

More ideas and pull requests are welcome!