Just one geek's opinions and epiphanies

Late but worth the wait...


I have finally updated my blog to Ghost 0.9.0, but I have to be honest, I think this might be the last version of my blog on Ghost. Now that I am working with Go much more, I am giving some serious consideration to using Hugo to generate a static build site. Not that my server is over burdened by the traffic I am getting, but the static site, for obvious reasons seems like a good way to go. Who knows?

Onward to the future!

Broadcast Communication in Golang


Today I set out to understand a few different ways of running an infinite loop with controlled stopping. Perhaps better known as a Daemon. Ok, daemons have lots of other features, but the gist is this... it's a loop, that accepts signals, and reacts upon them accordingly.

To that end, I came up with the code below for running a Daemon in Golang, that responds to UNIX Signals, and has multiple workers listening to the same channel and reacting in sync.

Note: The main function also alerts the works when to stop.

First, take note I called what I use a channel, that isn't 100% true. Yes, I use some channels, but the workers are actually listening to a sync.Cond, and reacting to the sync.Cond.Broadcast() method. You'll also note that I extended the sync.Cond struct into MySyncCond and added Payload and Kill fields. These allow me to not only wake the workers, but also tell them important information.

Hopefully my inline comment and docblocs do this justice, if not please feel free to reach out to me.

package main

import (
    "log" 
    "os" 
    "os/signal"
    "sync"
    "time"
)

// Extend the sync.Cond struct to add Kill, and Payload
type MySyncCond struct {
    *sync.Cond
    Kill    bool
    Payload int
}

// Add an Init() and set Kill to false (declaritive)
func (m *MySyncCond) Init() {
    m.Kill = false
}

// Extend sync.Cond.Broadcast() to allow a payload
func (m *MySyncCond) Broadcast(payload int) {
    // set the new payload before broadcasting
    m.Payload = payload
    // call sync.Cond.Broadcast()
    m.Cond.Broadcast()
}

// first worker, generates message to be broadcast out
func pinger(c *MySyncCond, wg *sync.WaitGroup) {

    defer wg.Done() // alert the waiting group we are done
    defer c.L.Unlock() // make sure we unlock the condition
    defer log.Println("Terminated pinger") // log a message that we are done here

    // Loop, and send out pings
    count := 0
    for {
        count += 1
        log.Printf("Pinger Pings: %d\n", count)
        // here I use the extended MySyncCond.Broadcast() that allows a payload to be passed
        c.Broadcast(count)
        time.Sleep(5 * time.Second)
    }
}

// our second worker, checks if the ping count is divisible by 1 (true)
// Note: The infinite loop only breaks when the sync.Cond has the field Kill == true
func divByOne(c *MySyncCond, wg *sync.WaitGroup) {
    defer wg.Done() 
    defer c.L.Unlock() // make sure we unlock
    defer log.Printf("Terminated divByOne")

    for {
        c.L.Lock()
        c.Wait() 
        // here I evaluate the Kill field of the MySyncCond struct
        if c.Kill {
            break
        }
        // here I evaluate the Payload field of the MySyncCond struct
        if c.Payload%1 == 0 {
            log.Printf("One\n")
        }
        c.L.Unlock()
    }
}

// basically the same as divByOne, but by 2
func divByTwo(c *MySyncCond, wg *sync.WaitGroup) {
    defer wg.Done()
    defer c.L.Unlock() // make sure we unlock
    defer log.Println("Terminated divByTwo")

    for {
        c.L.Lock()
        c.Wait()
        if c.Kill {
            break
        }
        if c.Payload%2 == 0 {
            log.Printf("Two\n")
        }
        c.L.Unlock()
    }
}

// yet another worker, this one divides by 3
func divByThree(c *MySyncCond, wg *sync.WaitGroup) {
    defer wg.Done()
    defer c.L.Unlock()
    defer log.Println("Terminated divByThree")
    for {
        c.L.Lock()
        c.Wait()
        if c.Kill {
            break
        }
        if c.Payload%3 == 0 {
            log.Printf("Three\n")
        }
        c.L.Unlock()
    }
}

// Where the magic starts
func main() {
    // write all the logs to a known file
    logfile, err := os.OpenFile("/tmp/condfun.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
    if err != nil {
        log.Fatal(err)
    }
    log.SetOutput(logfile)
    // make sure we close the file handler
    defer logfile.Close()

    // Create a new Lock (Mutext), pass it to a new sync.Cond, and the push the sync.Cond into MySyncCond (thus extending it)
    c := MySyncCond{sync.NewCond(new(sync.Mutex)),  false, 0}

    // our first channel, this is used to tell Main that the Waiter is done.
    done := make(chan bool)

    // Kill channel, used to catch Unix Signals
    kill := make(chan os.Signal)
    defer close(kill) // let's make sure to close the channel when we are done!
    signal.Notify(kill) // the magic here assigns Unix Signals to be passed to our channel

    var wg sync.WaitGroup // create a WaitGroup

    wg.Add(4) // tell our WaitGroup how many workers we have
    // fire off all our workers
    // each of these must call wg.Done() or else this whole thing falls apart, hence the defer wg.Done() they all call. To further enforce this we could have used and interface, but that seemed like real work
    go pinger(&c, &wg)
    go divByOne(&c, &wg)
    go divByTwo(&c, &wg)
    go divByThree(&c, &wg)

    // out anonymous worker, this waits for all things in the WaitGroup to complete, if that happens it will pass true to the done channel
    go func(wg *sync.WaitGroup, done chan bool) {
        defer close(done)
        wg.Wait()
        done <- true
    }(&wg, done)

    select {
    case <-kill: // handle the kill signal
        log.Println("Kill Seen")
        c.Kill = true
        c.Cond.Broadcast()
    case <-done: // handle the Waiter finishing (signals all workers stopped)
        log.Println("All workers have reported done")
    }

    log.Println("Terminated Main")
}

As things always are, as I am writing this I realize now that there is the potential for the workers to fail, and I don't handle the errors, but this is more about the synchronous communication between workers.

#talkpay: 13 years of my professional life


#talkpay has taken twitter by storm. I wanted to go a little longer form because I want to get the whole story of how I got to where I am at out there, and documented for everyone to see. I will not disclose my current salary, because I haven't been approved (I haven't asked) but it is considered confidential at my company.

I graduated from ITT Technical in September 11th, 2002. I had an associates degree in Computer Networking Systems Technology. I had given up on networking and decided to pursue a paycheck (read: things were tight, needed a job). I landed my first post-school job at Overstock.com as Customer Service Phone rep. I was making $10/hour. I soon helped the company start their first email response team. Manually typing out emails with links to best selling products. I had known HTML since middle school (6th grade) and I had been friendly with computers from earlier in life. My training in HTML and small scripting allowed me to knock out hundreds of emails a day, where most team members were completing 20 to 50.

This gave me opportunity to move into the Affiliate Marketing department, with a small raise to $11/hour. I spent a few years working in this department. I started as part of a 2 or 3 person team. Our 3rd basically showed the 2 of us how to do things, and then went on to better things. While there I learned PHP, JavaScript, some JSP, some Java, SQL, MySQL administration, Apache configuration, some Linux, and how to talk tech. When I finally left the company I was earning $15/hour and had a small chunk of Restricted Stock Units (RSUs).

I left that company for a whirl wind of stops at other small tech companies, each time increasing my salary. First at GrabTakeOut.com (no gone), WebStats (they were purchased by Adobe/Omniture), Lantis Fireworks (mostly non tech related work), SOS Staffing Inc (now Elwood Staffing), Code Greene, and Progrexion Marketing.

Each time I moved from one company to the next I would go to the internet, and find the salary range that is expected for the titles I would be taking. I would determine if I met the requirements of the job, if I knew the technologies, how well I knew the technologies, and how much I would have to learn quickly, to earn what I was asking. Never did I take a job with a salary request that I wasn't certain I was worth. I always asked for more than I felt I was worth, so that when the dust settled I would have what I thought I was worth. This is probably the most important part of all of this... I always got what I felt I was worth. My self worth was my target salary.

Once in my 13 years as a professional, I took a job offer, and was backed into a corner with my salary negotiation. It soured the entire experience at the company, and as a result I only stayed with the company a short time. Their expectation of my worth, was not held to my requirements. I walked. I got a MUCH better job, with a company that respects whole-heartedly their employees.

In 13 years I went from $20,800/yr in customer service to roughly 7x that, by simply continuing to learn, grow and expand my worth. Today I program in Python, to asist in Infrastructure Automation, with the title Senior Infrastructure Engineer. To get where I am today I never stopped learning. Today I am learning new things. Tomorrow I will learn more new things. I will never stop learning, becuase if I do, my worth will decline.

If you want to #talkpay that's fine, I have things to go learn so I can continue to #earnpay

The Interview


Spoilers ahead, you've been warned.

I had the opportunity tonight to watch The Interview starring James Franco, and Seth Rogen. There has been a lot in the news lately about this movie, and the chaos surrounding the films release. If you aren't familiar with it all, well, this isn't a recap of all that. This is my thoughts on the movie.

The long and short of it is this, people are really hating on the movie. I am sure most of this conversation is because of all the hype the movie had, with the press, and actions in cyber security surrounding the film. Basically, it was another Seth Rogen and James Franco movie.

If you saw This is the End, that also starred Seth Rogen, and James Franco, then you basically saw The Interview. Except instead of rapture, it's Kim Jong Un. The two Americans are teamed up in an awkwardly homosexual relationship and they are thrown against odds to defeat some super power. They experience drugs and they ultimately conquer something.

If you are expecting an amazing film with a lot of laughs, then this probably isn't the film for you. If you are looking for a few laughs, some carnage and Seth Rogen being awkwardly persued by James Franco, then this is the movie for you.

I rather enjoyed the film, but I think it was because I was expecting it to suck more than it did. All in all, good film.

Fedora 21 Launched!


Well, Fedora 21 is in the wild... time to update my workstation. Back in a while