Archive for the ‘Cube Life’ Category

Interesting Rounding Issue in Clojure

Friday, August 12th, 2016

Clojure.jpgWe have a shared library that has a lot of really useful functions in it for all the apps we build, and one of the really useful things is to round to a fixed number of decimal places. Now, this isn't Rocket Science, but it is nice to have written well once, and then not have to mess with ever again.

The trick is that you need it to be fast for all Clojure datatypes as well. And several of the more common implementations aren't very fast due to boxing and the overhead associated with that. So we made some pretty simple little functions:

  (defn to-2dp
    "Function to simply resolve a number to a decimal (double), and then round
    it to 2 DP and return it. Pretty simple, but very useful."
    [x]
    (/ (math/round (* 100.0 (parse-double x))) 100.0))
 
  (defn to-3dp
    "Function to simply resolve a number to a decimal (double), and then round
    it to 3 DP and return it. Pretty simple, but very useful."
    [x]
    (/ (math/round (* 1000.0 (parse-double x))) 1000.0))

but we had a problem. Some of the inputs gave us a lot of grief because of some nasty rounding. And it wasn't all values - it was just those values that had a hard time being expressed in the floating point format.

The solution was to accept some of the inefficiency of rat eJVM BigDecimal and the advanced representation it had, and use that:

  (defn to-2dp
    "Function to simply resolve a number to a decimal (double), and then round
    it to 2 DP and return it. Pretty simple, but very useful."
    [x]
    (/ (math/round (* 100.0 (bigdec (parse-double x)))) 100.0))
 
  (defn to-3dp
    "Function to simply resolve a number to a decimal (double), and then round
    it to 3 DP and return it. Pretty simple, but very useful."
    [x]
    (/ (math/round (* 1000.0 (bigdec (parse-double x)))) 1000.0))

and then things settled down.

Sometimes Life is Unpopular

Tuesday, May 3rd, 2016

Bad Idea

This morning I was dealing with yet another change to some clojure functions that are in a library that we have created at The Shop to make it easy to make the services that we make, and easy for new clojure developers to wrap their heads around. It's not perfect, or ideal, but it does strike a nice balance between purely functional code and re-use and state in the library.

Face it - connection pooling is state. It needs to exist, but it's not what you'd like to have if you could avoid it. So you bury it in a library, so that the new clojure developers can use this library with the connection pools clearly hidden off screen doing their thing.

It's a compromise. But one that favors the new clojure developer. And most days, that's the folks I have to work with. They typically know Java, or C#, but it's a rare individual that comes into the shop with a lot of clojure experience, and usually they "get it" about the decisions, and it's something they pretty easily adapt to.

But then there are others.

Today I've been dealing with Steve. Steve is a C# developer that hates his job. He's hated it for a long time. He hates the language he's using (C#). He hates the tools he is having to learn to do his job. He pretty much hates everything about his job.

But he wants to work in clojure.

Yet he's not really interested in learning the landscape. He wants to change it to be how he thinks is should be. I don't blame him - he questions a lot because he trusts no one. Having asked him this direct question, and receiving the affirmative response, I know beyond a shadow of a doubt, this is exactly who he is. But to not learn what is before suggesting what should be is a little short-sighted.

That he's annoying about it is just "icing on the cake" for me.

So this morning I decided that emotion has no place in this conversation. Neither does his opinions. I have listened, I have tried to explain, I have listened more, and tried to show why this is the path we have taken. All of this is immaterial to him, and so in the end, I have to make a decision, list the reasons for this decision, and then move on.

I don't relish being a jerk. Or a dictator. But at this point, more conversation is wasting time. It's time to accept that this is the paradigm we are working under, and that's it.

I do not expect him to like it.

Another Month in the Books

Friday, April 29th, 2016

Path

I don't claim to know what the future will bring. I'm doing my best to just make it day-to-day most of the time. I'm lucky in that I have a decent job, and it affords me a nice distraction - most of the time. I have a support system that seems to work pretty well, and on the whole, I am making it day-by-day.

I was reading about the Five Stages of Grief and one of the things that really amazed me was that when talking about the final step - Acceptance - "...a gift not afforded to everyone.", it is marked by a withdrawal and calm.

I don't know why I was surprised to read this - I mean, I'm going through a loss, but I expected to "find myself" in one of the previous stages. And to be honest, I think I find moments of anger, or bargaining, from time to time - but I always end it with the resignation that it would never work, and for better or worse, this is my path.

Finding out that not everyone reaches Acceptance was also a bit of a shock - because as they say your life is never the same. Never. It's just a new "normal", and you make new routines, and new paths, and you get on with living.

Anyway... it's another month in the books, and I'm still here.

Sharing Login Auth Tokens

Saturday, January 30th, 2016

Javascript

Over the past several months we have built several Clojure/Javascript apps at The Shop, and each one has needed to have some level of authentication, and thankfully, that's all been provided by the Security Service that was already built and maintained by another guy at The Shop. It works, what else would we need? You get authentication tokens (UUIDs) and you can get information about the user based on the token, and you can't really guess an auth token for a user, so they are pretty decently secure.

But they were app-specific because we tied them to the cookies on the pages, and those pages were all from a site that was different for each app we were building. This meant that you'd have to login to three, four apps a day with the same credentials, and still not have a clean, guaranteed way of invoking a page in one app from a page in another.

This is what I wanted to solve.

This morning, it came to me - there had to be a way to store the auth tokens in a cookie that was across the apps, and then just use that as a standard for all apps. Simple. Since it was still secure (enough), we didn't have to worry about folks yanking the token, or even knowing who the token belonged to.

The question was How?

Thankfully, jQuery has this all solved with the domain: tag in the $.cookie() function. All we needed to do was to have a consistent upper-level domain for the apps that also worked for local development.

What I came up with was a simple function:

  function loginDomain() {
    var here = document.domain;
    if (here === 'localhost') {
      return here;
    }
    var parts = here.split('.');
    parts.shift();
    return parts.join('.');
  }

so that we would have a single login for the production hosts on theshop.com and another for the development hosts on da-shop.com. This would work perfectly if I could update all the places that the cookie was accessed.

What I learned was that the accessing of the cookie did not need to have the domain: tag, but setting it - or deleting it did. So for example, to read the token, I'd say:

  var tok = $.cookie('shop-auth-token');
  if (tok) {
    // ...
  }

and no matter where the cookie was saved, if it's in the "domain path", then it's OK. But when I needed to set the cookie I'd need to:

  var exp = new Date();
  exp.setTime(exp.getTime() + (8 * 60  * 60 * 1000));
  $.cookie('shop-auth-token', tok, { domain: loginDomain(),
                                     expires: exp );

and when I needed to invalidate it:

  $.removeCookie('shop-auth-token', { domain: loginDomain() });

Once I got these all set up, and in each application, one login on the specific login domain would then be successfully used on all apps in that domain with this code. Sweet! It worked like a charm, and it took me about 90 mins to figure this out, and put it in all the apps we have.

Big win!

Indexing Dates in Postgres JSONB Data

Wednesday, November 25th, 2015

PostgreSQL.jpg

Postgres is an amazing database. With 9.4's JSONB datatype, and a little clojure code, I can save data structures to and from Postgres, and have them query-able by any JDBC client. It's just amazing. But recently we had a problem at The Shop where we needed to index a field in a JSONB element, but it was a date, and so we ran into the problem of Immutable Functions.

Simply trying to create an index on the field:

  CREATE INDEX idx_d ON my_table ((fields->>'DateStarted'))::DATE;

didn't work with Postgres saying you can't have a mutable function in the creation of the index. So it was time to dig out the browser and look up what's going on. Sure enough, this was something well-known, and all involving the conversion of dates by locale, and why that was not allowed for an index.

Makes sense, but I still needed an index because the queries using this field in the WHERE clause were taking 6+ min, and we needed to really bring them down - like way down. Then I found that you could make functions that were tagged as IMMUTABLE and so made these:

  CREATE OR REPLACE FUNCTION mk_date(src VARCHAR)
  RETURNS DATE
  immutable AS $body$
    SELECT src::DATE;
  $body$ LANGUAGE SQL;
 
  CREATE OR REPLACE FUNCTION mk_datetime(src VARCHAR)
  RETURNS TIMESTAMP
  immutable AS $body$
    SELECT src::TIMESTAMP;
  $body$ LANGUAGE SQL;

the reason I chose to use ::date and ::timestamp is that they handle NULL values very well, and the corresponding functions don't do that so well.

With these functions, I was then able to create the index:

  CREATE INDEX idx_d ON my_table (mk_date((fields->>'DateStarted')));

and then as long as I used the same functions in my WHERE clause, everything was great.

The result was a 1000x increase in speed - 319,000 msec to 317 msec. Amazing.

I do love Postgres.

Just Slogging through Stuff

Friday, October 30th, 2015

Path

There are days that I don't know how I'll make it through the day back to my little house, and the one place that I really feel safe. And I'm continually surprised that every day around 6:00-ish, I find myself looking at that facade, and unlocking the door, and walking in and feeling an amazing sense of relief. I'm not better - I'm just a little bit stronger. I can take the heartache and pain that seems to follow my day-to-day existence.

A very nice friend of mine once said "It either gets better, or you get used to it. Either way, it seems like it's better." Not bad advice. It's not something that's easy to heed, but it is true nonetheless. Very true, indeed.

So this past month has been a lot more more of getting used to it than it getting really better. Just gotta keep slogging through the muck...

Slow Starts but Strong Finishes

Wednesday, July 8th, 2015

cubeLifeView.gifOver the course of the last week or so I've noticed that I'm having a really hard time getting motivated in the morning. I'm running well - and that's nice, but it's after that - when I leave for the train, and then work. It's just hard feeling that I'm heading out to do something worthwhile and good.

More like just do what I needed to collect a paycheck. And I hate that feeling.

Yet at about 4:00 pm, when the workday is almost over, I'm looking back at what I accomplished during the day and it's simply amazing! It's like I'm able to forget about all the things that had me down at the start of the day, and the things I'm able to do with the code I'm working on make me smile and laugh -- all the time!

So I'm trying to figure out what it's all about. Why the slow start, and strong finish? Why can't I look forward to the work so that my day starts with excitement? If I know I'm heading for a day that I'm going to do great, fun things - then why can't I use that as motivation at the start of the day?

I have a feeling it's related to The Three-Martini Lunch.

Specifically, I'm not really sure what's happening at The Shop right now. Yes, there's work, for now, but it could all end on Aug 1 - when I deliver this app I'm working on. They have plans, but it's all still awaiting approval by upper-management, and that, to me, means that it's still up for debate as to what will be done.

If that's the case, then I could find myself maintaining one little clojure app - and some .NET code (shudder) in just no time at all.

Yet when I'm able to ignore all that... forget the place I'm working, and just focus on the problems at the keyboard, then I can start solving problems, and delivering value. That makes me happy. It's like work - the real act of solving probelems, using clojure and it's amazing toolset, allows me to forget the realities of my current situation.

Clojure is my mid-afternoon drinking.

It doesn't solve anything. It's just getting drunk to avoid the problems you're facing. Tomorrow you'll be sober, and it'll all come back to you. So you dive into the "bottle" (work) and don't come up for air.

I'm not sure this is the correct analogy, but I have a feeling it's close. I love the work, and the work is what I find enjoyment from. But the place seems to be having a hard time with focus, and that concerns me.

Not much I can do about it, so it's likely to continue for a while.

SSL in Jetty (Compojure)

Monday, July 6th, 2015

Clojure.jpgI've been working on a project at The Shop and it's a simple-ish clojure back-end with a Javascript/jQuery/Bootstrap front-end, and it's not too bad. Getting there. But recently, it was time to add in the user authentication code, and that meant passwords. That meant security, and simple HTTP requests weren't going to do. We had to have security. HTTPS. So I dove down the rabbit hole of getting Jetty 7 to run as SSL only within a compojure/ring application.

What I found was that there really wasn't a wealth of information on the subject. Most folks expected a front-end service like nginx to handle the SSL, and then forward on to the clojure app requests on simple port 8080. While this is possible, it also means that port 8080 is there listening, and it's possible to have an intercept on that port and all of a sudden we're not so secure.

Nope. It needed to be Jetty on SSL on port 8443. Period.

Get the Certificate File

No doubt, getting the initial certificate file is the hardest part for most folks, as it's all about money. But I suppose self-signed certificates are useful - to a point, but being in a commercial organization, it's nice to see that there's a real certificate file, and all I need to do is to convert it to a Java Key Store file. In this case, it started as a pox file - a PKCS formatted certificate. Then:

  $ keytool -importkeystore -srckeystore my_cert.pfx -srcstoretype pkcs12 \
      -destkeystore my_cert.jks -deststoretype jks -deststorepass MY_PASSWORD

where it's clear what the file names are, and passwords. This gets you a my_cert.jks file that's what you are going to need. The MY_PASSWORD is also important as it will have to be in the code as well to read the file.

Put the KeyStore File in resources/certs

Like everything additional in a Meiningen project, this needs to go into the resources directory, and for convention, I'm throwing it into resources/certs as it's not the only thing in my resources directory for this project, and a little separation is a good thing.

Convert the Code to Dual-Mode

Working with the following dependencies in the project.clj file:

    ;; command line option processing
    [org.clojure/tools.cli "0.2.2"]
    ;; web server
    [compojure "1.3.4"]
    [ring/ring-core "1.3.2"]
    [ring/ring-jetty-adapter "1.3.2"]
    [ring.middleware.jsonp "0.1.6"]
    [ring/ring-defaults "0.1.5"]

and assuming you have a it running on port 8080, it might look a little like this:

(ns myapp.main
  (:require [clojure.java.io :refer [resource input-stream]]
            [clojure.tools.cli :refer [cli]]
            [clojure.tools.logging :refer [error info infof]]
            [myapp.server :refer [app]]
            [ring.adapter.jetty :as jt])
  (:import java.security.KeyStore)
  (:gen-class))
 
  (defn handle-args
    "Function to parse the arguments to the main entry point of this
    project and do what it's asking. By the time we return, it's all
    done and over."
    [args]
    (let [[params [action]] (cli args
               ["-p" "--port" "Listen on this port"
                 :default 8080 :parse-fn #(Integer. %)]
               ["-v" "--verbose" :flag true])]
      (cond
        (= "web" action)
          (jt/run-jetty app { :port (:port params) }))
        :else
          (do
            (info "Welcome to My App!")
            (println "Welcome to My App!")))))
 
  (defn -main
    "Function to kick off everything and clean up afterwards"
    [& args]
    (with-error-handling (handle-args args)))

this will assume port 8080, and yet allow the command-line args to override this. In order to add the SSL component to this, we simply have to add a few options to Jetty:

  (defn handle-args
    "Function to parse the arguments to the main entry point of this
    project and do what it's asking. By the time we return, it's all
    done and over."
    [args]
    (let [[params [action]] (cli args
               ["-p" "--port" "Listen on this port"
                 :default 8080 :parse-fn #(Integer. %)]
               ["-s" "--ssl-port" "Listen on this port"
                 :default 8443 :parse-fn #(Integer. %)]
               ["-v" "--verbose" :flag true])]
      (cond
        (= "web" action)
          (jt/run-jetty app { :port (:port params)
                              :ssl? true
                              :ssl-port (:ssl-port params)
                              :keystore "resources/certs/my_cert.jks"
                              :key-password "MY_PASSWORD" }))
        :else
          (do
            (info "Welcome to My App!")
            (println "Welcome to My App!")))))

Start this guy now, and it'll answer on port 8080 for normal HTTP traffic, and port 8443 for HTTPS traffic. So far, so good. But there's a catch here, and we'll get to it soon.

Make it SSL Only

The real request was to make is SSL-only, so we to remove the port 8080 traffic, but we can't just tell Jetty not to run that one - we have to actively remove it. Thankfully, composure allows us this flexibility. If we make a function:

  (defn remove-non-ssl-connectors
    "Function to configure the Jetty instance to remove all non-SSL
    connectors so that there's **no way** to get into this service BUT
    by SSL (https)."
    [server]
    (doseq [c (.getConnectors server)]
      (when-not (or (nil? c)
                    (instance? org.eclipse.jetty.server.ssl.
                                    SslSelectChannelConnector c))
        (.removeConnector server c)))
    server)

then we can add that as an option to Jetty to tell it to run on it's configuration:

  (defn handle-args
    "Function to parse the arguments to the main entry point of this
    project and do what it's asking. By the time we return, it's all
    done and over."
    [args]
    (let [[params [action]] (cli args
               ["-p" "--port" "Listen on this port"
                 :default 8080 :parse-fn #(Integer. %)]
               ["-s" "--ssl-port" "Listen on this port"
                 :default 8443 :parse-fn #(Integer. %)]
               ["-v" "--verbose" :flag true])]
      (cond
        (= "web" action)
          (jt/run-jetty app { :configurator remove-non-ssl-connectors
                              :port (:port params)
                              :ssl? true
                              :ssl-port (:ssl-port params)
                              :keystore "resources/certs/my_cert.jks"
                              :key-password "MY_PASSWORD" }))
        :else
          (do
            (info "Welcome to My App!")
            (println "Welcome to My App!")))))

and now if you restart the app, it won't answer on port 8080, but it will still answer on port 8443 with HTTPS. Getting very close.

Making it Deployable in an Uberjar

The final wrinkle is that the :keystone option is a location in the filesystem of the key store file. That's not good for deployments because it means that while the keystore file will be packaged up in the uberjar, it's not going to be referenced that way - and it will have to also exist in the filesystem in the same relative location.

This stinks.

So I did some more digging, and ring-jetty had what I needed - the ability to pass it a java.security.KeyStore instance. Now I needed to read the keystone file from the jar, into an instance of that object, and pass it in.

Start with:

  (defn load-keystore
    "Function to load the SSL KeyStore from the resources so that it's ready
    to be used to run the SSL connections for Jetty. This is a preferred method
    to having just a path for locating the certificate, as this allows the cert
    to be _included_ in the uberjar itself."
    [loc pw]
    (if (and (string? loc) (string? pw))
      (doto (KeyStore/getInstance (KeyStore/getDefaultType))
        (.load (input-stream (resource loc)) (char-array pw)))))

which will load a keystone file from the uberjar - placed in the resources directory. This was a major find for me, and made it all possible. That it only takes a few lines of clojure and java is just amazing.

Now we can put it all together:

  (defn handle-args
    "Function to parse the arguments to the main entry point of this
    project and do what it's asking. By the time we return, it's all
    done and over."
    [args]
    (let [[params [action]] (cli args
               ["-p" "--port" "Listen on this port"
                 :default 8080 :parse-fn #(Integer. %)]
               ["-s" "--ssl-port" "Listen on this port"
                 :default 8443 :parse-fn #(Integer. %)]
               ["-v" "--verbose" :flag true])]
      (cond
        (= "web" action)
          (let [loc "certs/my_cert.jks"   ;; SSL keystore location
                pw "MY_PASSWORD"]         ;; ...and password
            (jt/run-jetty app { :configurator remove-non-ssl-connectors
                                :port (:port params)
                                :ssl? true
                                :ssl-port (:ssl-port params)
                                :keystore (load-keystore loc pw)
                                :key-password pw }))
        :else
          (do
            (info "Welcome to My App!")
            (println "Welcome to My App!")))))

Got a Call… from a Guy… about Clojure…

Wednesday, May 13th, 2015

Clojure.jpg

Yesterday I got a quick note from an ex-co-worker about how he might go about writing a micro-service in Clojure. Seems his CTO is a big functional programmer, and as such, he wanted my friend to write something in Clojure. So he reached out to the only person he knew that had actually done that, and so I got the call.

I was happy to help him along. I sent him links to Gists about how a server.clj file might look, as well as how to handle JSONP - assuming that the calls were coming from jQuery, and basically, just make sure that things got off on the right foot.

I also gave him some pointers about the standard libraries that I've been using for clojure work - cli-time, composure, cheshire, carmine, etc. They make it a lot easier, and a lot of folks are using them - so it's good to get started using the standard stuff right off the bat.

We talked a lot about getting started, and how the RESTful stuff was going to look, and then he said he'd dig into it when he had time. But the most impressive part was the recognition that by simply saying "Clojure Developers Wanted" - the CTO knew he'd get all kinds of hits, and all kinds of street cred to make the most simpler.

Smart man... deserves to be in that position.

Clash of the (Non-)Titans

Tuesday, May 12th, 2015

Crazy Lemon the Coder

So I just got out of a meeting with my manager's manager. My manager is trying to rebuild an existing system - as opposed to simply augmenting it, and the way he's going about doing this is by using AWS services and a flock of 11 consultants. They are skipping what is, and jumping right to what he wants, and in so doing, leaving all the employees of the company that might offer help, support, and certainly ongoing maintenance - completely in the dark.

I've asked for details about the project I'm supposed to be on, and the response was always "None of your concern." OK... you're the manager. After repeated attempts to talk to HR, and seeing nothing done about it, I've simply taken the stand that I'll be very up-front about the fact that I'm doing nothing, as long as I'm given nothing to do. That way people can choose to see that as an anomaly - or not. Their choice.

I'm being paid either way. I'm very transparent with HR so that should it come back to me that I was wasting company resources (my time), that would be documented as not true. So I'm covered.

I don't think that using AWS is wrong. It's just a different approach than what I know the CTO has expressly stated: It just costs too much. It's more, but if you factor in the cost of having a datacenter, and racks, and machines, and people to maintain them, and then people to install, upgrade, monitor and fix the services like AWS offers, well... I'm not certain that all cases are clearly cheaper - one way or the other. I really think that the costs are coming down, and the service levels are to the point that you need to consider it on a case-by-case basis to be 100% certain.

But that's just my opinion, and not the CTO's. And he has made himself very clear on the matter. So for my manager (a consultant) to come in and make the plan, and hire the consultants to execute this plan that is 100% AWS... well... I knew it'd be a point of contention, and told my manager this when he first showed me his plans.

"Not your concern. That's something I'll make happen."

OK, boss... I tried to tell you. As my GrandPa Bowen said:

Those who don't listen, must feel.
-- Don Bowen

so he's going to have to feel it.

Part of that seems to be happening in the meeting I just got out of. The CTO said that AWS was not going to happen. That the enhancement of the existing system was the job, and that I was going to be brought in - as well as the other employees that have a solid understanding of the existing system. Makes good sense to me.

But it's 180 degrees from what my manager has been doing, planning, and moving towards.

So starting tomorrow I can see a new round in the fight. There's no way that the current plan is going to be allowed. So what value are the AWS consultants? My manager is being forced to bring in people he didn't want to bring in - and actively excluded and rejected. How hard are we going to work for this guy? Why should we? He was ready to drop us all - and told me as much less than a week ago. So how is he going to make this happen?

At the same time, the first round went to him in hiring the consultants, and starting this work... but this last round went to the CTO who shut down a $2 million deployment because we already had 90% of it in-house, paid for, and supported. It would be a colossal waste of money.

I'm no fool... this fight isn't over, just a temporary lull in the action. Soon there will be more slings and arrows, and we that are caught in the middle will no doubt soon tire of this, and let the (non-)Titans battle it out amongst themselves.

But is this any way to run a company?