Halting the callback chains

On this article I will share a technique to find out more about callbacks in Ruby on Rails framework.

Callbacks are a common way of implicitly invoking actions when an event fires inside a system. Many web frameworks like Rails or Laravel depend on this mechanism to handle esoteric events. A chain of callbacks is created when multiple callbacks are being fired one after another. Finally, we will call halting such a chain when we stop the event propagation.

Halting a callback in Rails is simple if we use the abstraction provided by the framework. Specifically, calling

throw :abort

will halt the ActiveRecord callbacks.

In larger Rails systems where legacy code or questionable techniques, like depending in implicit ActiveRecord callbacks for domain specific logic, may have been accumulated, halting and debugging callbacks is not a trivial task. One way to approach such troubleshooting is by learning more about the callbacks of our system. To do that we will use the pry debugger.

The class methods we are looking for is:

_#{action_name}_callbacks # e.g., _destroy_callbacks

Then using select, map and filter I am able to list and iterate over any callback given an ActiveModel class:

User._destroy_callbacks.select {|cb| cb.kind == :after }.map(&:filter)

=>
[
  :destroy_user_friends,
  :build_ast,
  :disconnect_from_bank_system,
  :check_soft_delete,
  ...
]

tshark and tcpdump

Occasionally we work with interesting network bugs or we want to learn more about how our service behaves outside of the application layer. Two tools that help me in those occasions are tshark and tcpdump.

Installation

Let’s assume we work on a unix system. Then to install tcpdump we simple execute:

$ apt install tcpdump

Installing tshark is simple too:

$ apt install libcap2-bin tshark

Example API

As a demonstration, we are going to utilize a clean and small Sinatra server. For this example a working Ruby installation is required. See the official docs on how to install Ruby properly on a local machine: https://www.ruby-lang.org/en/documentation/installation/

Going back to our example, first, create the repo:

$ mkdir booklist && cd booklist

Install sinatra:

$ gem install sinatra

Create the example API:

$ cat server.rb

require 'sinatra'
require 'json'

get '/' do
["pragmatic programmer", "clean code"].to_json
end

Fire up the server:

$ ruby server.rb

== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...
* Version 3.12.0 (ruby 2.4.0-p0), codename: Llamas in Pajamas
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:4567
Use Ctrl-C to stop

It works:

$ curl -X GET localhost:4567 | jq .

[
"pragmatic programmer",
"clean code"
]

tcpdump

tcpdump is available on most unix systems, so we can use it on a small remote sever where tshark would be most probably an overkill. It provides decoding so we can investigate how our services interact with the network.

We will work with the lo interface for this example:

$ tcpdump -D

Execute:

$ sudo tcpdump -i lo -A

Hit the service:

$ curl -X GET localhost:4567

Now notice that tcpdump has captured the traffic:

HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 37

["pragmatic programmer","clean code"]

tshark

tshark is a powerful sniffer with many filters which can decode traffic and provides tools for running more complex analysis on it.

tshark can behave exactly like tcpdump:

$ tshark -i lo --color

Depending on the problem, the best solution may be to combine the powers of these tools. A usual case is to create a file with captured decoded traffic with tcpdump and then run analysis on the file with tshark. Or just do both with tshark.

Let’s explore how to do this on the previous example.

Capture the traffic to a packet capture(pcap) file:

$ touch dump
$ tshark -i lo -w dump.pcap

Analyze it:

$ tshark -r dump.pcap

For example, HTTP analysis:

$ tshark -r dump.pcap -Y http.request -T fields -e http.host -e http.user_agent | sort | uniq -c | sort -n

We could also use wireshark for the last step which provides a nice GUI.

Outro

Learning more about these tools has helped me analyze and research solutions on more complicated problems, which in turn help me grow as an engineer and problem solver. Some good references for learning more about these tools are:

1) tutorial: https://danielmiessler.com/study/tcpdump/
2) how tcp works: https://medium.com/@eranda/analyze-tcp-dumps-a089c2644f19
3) book: https://www.goodreads.com/book/show/505564.The_TCP_IP_Guide

Git bisect debugging

Git is being used primarily for version control but it also provides some generic tools for debugging. In this article, we will
explore how git bisect can help us firstly spot a commit that introduced a but in our codebase and secondly track it throughout
it’s lifespan.

What is git bisect

From the official docs:

The bisect command does a binary search through your commit history to help you identify as quickly as possible which commit
introduced an issue.

Read more at https://git-scm.com/book/en/v2/Git-Tools-Debugging-with-Git.

The gist is that git bisect provides feedback about where and when the bug was introduced.

Example

First of all, we start by running:

$ git bisect start

A binary search is being initialized and we have to answer yes or no, which usually means answer whether the commit
was error free or not.

We do this by marking commits as bad or good:

$ git bisect bad
$ git bisect good

We also get some feedback along the way:

# => Bisecting: 6 revisions left to test after this

When we are done with our research we can easily get back to a working state:

$ git bisect reset