Queries on local so much faster than queries on server

I was encountering a situation where big aggregating queries that took seconds on local were taking minutes or even hours on prod. After trying about a million things this Stack Exchange post finally cleared things up.

Setting innodb_buffer_pool_size = 5G in MariaDB’s configuration and restarting instantly changed the queries to taking seconds. The default is 128M which is … not useful 🙂

Change Terminal Background Color When ssh’ed – Mac

Save this as “ssh” somewhere on your $PATH ahead of /usr/bin/ssh and make it executable

#!/bin/sh

HOSTNAME=$(echo $@ | sed 's/.*@//')

set_theme () {
    osascript -e "tell application \"Terminal\" to set current settings of first window to settings set \"$1\""
}

on_exit () {
    set_theme "Solarized Light"
}

trap on_exit EXIT

case $HOSTNAME in
  # These all do the same thing but we could have different themes for different locations
  hb|pd|decksite) set_theme "Solarized Dark" ;;
  *) set_theme "Solarized Dark" ;;
esac

/usr/bin/ssh "$@"

Set Up a New Mac (August 2023 Edition)

Previously:
July 2019,
July 2022

  • Upgrade to latest OSX.
  • Restore u directory from Backblaze backup. Restore dotfiles, .ssh dir, .vim dir, maybe other dot directories from Backblaze backup. Restore ~/Library/Application Support/Alfred 4/*.
  • Alfred (and give it Accessibility access, add license key).
  • Set new hostname with:
    # Respectively, fully-qualified hostname, Bonjour hostname, the user-friendly computer name you see in Finder, flush the DNS cache …
    $ sudo scutil --set HostName agrajag.bluebones.net
    $ sudo scutil --set LocalHostName agrajag
    $ sudo scutil --set ComputerName agrajag
    $ dscacheutil -flushcache
    
  • Dock: remove everything, hiding on, magnification on.
  • Sublime Text 4 (add license key, set "trim_trailing_white_space_on_save": true in sublime prefs, install Package Control, install Solarized Theme).
  • subl commandline sublime.
    $ ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" ~/u/bin/subl
  • sublime-git-ignorer: (1) add Package Control repo https://github.com/apc999/sublime-text-gitignore (2)
    add package sublime-text-gitignore (3) use menu item : File->Exclude Git-ignored (4) Give this a keyboard shortcut in Sublime, Preferences, Key Bindings with { "keys": ["super+shift+i"], "command": "exclude_ignored" }
  • Keyboard shortcut to toggle word wrap in Sublime Text. Preferences, Key Bindings { "keys": ["super+shift+w"], "command": "toggle_setting", "args": { "setting": "word_wrap" } }
  • Install FiraCode and add it to Sublime Text prefs with "font_face": "Fira Code".
  • Hide Desktop icons
    $ defaults write com.apple.finder CreateDesktop false
    $ killall Finder
  • Solarized theme for Terminal and make default in preferences.
  • “Use Option as Meta Key” in Terminal to make left Alt work as Esc.
  • Terminal, Settings, Advanced, un-check “Audible bell” and “Only when sound is muted”
  • System Preferences, Sound, turn “Alert volume” all the way down
  • Use Ctrl-f7 to enable keyboard navigation of dialogs
  • Homebrew. (Installs OSX commandline tools.)
  • brew install mariadb
  • brew install npm
  • brew install apache2. Docker Desktop (below) uses port 8080 so edit /opt/homebrew/etc/httpd/httpd.conf to use port 8081 and remember that you did it.
  • brew install php. Enable in Apache with LoadModule, FilesMatch and DirectoryIndex directives as per homebrew output and brew services restart apache2.
  • brew install gh
  • brew install composer
  • Docker Desktop.
  • Discord.
  • Change all .txt to open with Sublime Text.
  • Show hidden files in Finder. Cmd-Shift-.
  • WhatsApp.
  • 1Password.
  • Chrome. (Log in to Chrome to get uBlock Origin and other extensions.)
  • Skitch.
  • Backblaze.
  • Parallels Desktop + MTGO
  • npm install -g rtm-cli
  • Disable Ask Siri in System Preferences, Siri
  • Turn on “tap to click” in System Preferences, Trackpad.
  • Disable long-press (it’s wayyyyy too annoying when selecting text in Chrome): defaults write -g ApplePressAndHoldEnabled -bool false
  • Zoom.
  • Skype.
  • IntelliJ IDEs for languages that you’re currently using from the list below. Install Solarized theme and switch to Fira Code as editor font for each.
    • PyCharm.
    • GoLand.
    • WebStorm.
    • PHPStorm.
  • Spotify.
  • Slack.
  • git clone important repos (ff, pd, server)
  • Restore any local databases for dev (ff, decksite, logsite)
  • ln -s ~/ff/src/www /opt/homebrew/var/www/ff
  • GRANT ALL ON ff.* TO ff@localhost IDENTIFIED BY *REDACTED*
  • pd setup

The 138 Terminal Positions of Tic-Tac-Toe

Also known as Noughts and Crosses.

A Rust program to generate all terminal positions of the game of Tic-Tac-Toe. Rotations, transpositions, games-still-in-progress and impossible board states are not shown.

github.com/bakert/boards

use std::collections::HashMap;

const EMPTY: char = ' ';
const X: char = 'X';
const O: char = 'O';

type Boards = HashMap;
type Board = [[char; 3]; 3];
type Representations = [String; 8];

trait BoardMethods {
    fn display(&self) -> String;
    fn is_terminal(&self) -> bool;
    fn representation(&self) -> String;
    fn all_representations(&self) -> Representations;
    fn rotate(&self) -> Board;
    fn transpose(&self) -> Board;
}

trait BoardsMethods {
    fn contains(&self, b: Board) -> bool;
    fn insert_board(&mut self, b: Board);
}

fn main() {
    test();
    let empty_board: Board = [[EMPTY; 3]; 3];
    let mut final_boards: Boards = HashMap::new();
    let boards: Vec = all_boards(empty_board, X);
    for board in boards {
        if board.is_terminal() && !final_boards.contains(board) {
            final_boards.insert_board(board);
        }
    }
    println!("{} unique terminal boards found\n", final_boards.len());
    for (_, board) in final_boards {
        println!("{}", board.display());
    }
}

fn all_boards(board: Board, to_play: char) -> Vec {
    let to_play_next: char = if to_play == X { O } else { X };
    let mut boards: Vec = Vec::new();
    for x in 0..board.len() {
        let row = board[x];
        for y in 0..row.len() {
            if board[x][y] == EMPTY {
                let mut b: Board = board.clone();
                b[x][y] = to_play;
                boards.push(b);
                if !b.is_terminal() {
                    boards.append(&mut all_boards(b, to_play_next));
                }
            }
        }
    }
    return boards;
}

impl BoardMethods for Board {
    fn display(&self) -> String {
        let mut s = String::new();
        for row in self {
            for square in row {
                s.push('|');
                s.push(*square);
            }
            s.push_str("|\n");
        }
        return s;
    }

    fn is_terminal(&self) -> bool {
        for i in 0..3 {
            if self[i][0] != EMPTY && self[i][0] == self[i][1] && self[i][1] == self[i][2] {
                return true;
            }
            if self[0][i] != EMPTY && self[0][i] == self[1][i] && self[1][i] == self[2][i] {
                return true;
            }
        }
        if self[0][0] != EMPTY && self[0][0] == self[1][1] && self[1][1] == self[2][2] {
            return true;
        }
        if self[0][2] != EMPTY && self[0][2] == self[1][1] && self[1][1] == self[2][0] {
            return true;
        }
        for row in self {
            for square in row {
                if *square == EMPTY {
                    return false;
                }
            }
        }
        return true;
    }

    fn all_representations(&self) -> Representations {
        let mut r: Representations = Default::default();
        r[0] = self.representation();
        r[1] = self.rotate().representation();
        r[2] = self.rotate().rotate().representation();
        r[3] = self.rotate().rotate().rotate().representation();
        r[4] = self.transpose().representation();
        r[5] = self.rotate().transpose().representation();
        r[6] = self.rotate().rotate().transpose().representation();
        r[7] = self.rotate().rotate().rotate().transpose().representation();
        return r;
    }

    fn representation(&self) -> String {
        let mut s = String::new();
        for row in self {
            for square in row {
                s.push(*square);
            }
        }
        return s;
    }

    fn rotate(&self) -> Board {
        let mut b: Board = [[EMPTY, EMPTY, EMPTY]; 3];
        for x in 0..self.len() {
            let row = self[x];
            for y in 0..row.len() {
                b[x][y] = self[row.len() - y - 1][x];
            }
        }
        return b
    }

    fn transpose(&self) -> Board {
        let mut b: Board = [[EMPTY, EMPTY, EMPTY]; 3];
        for x in 0..self.len() {
            let row = self[x];
            for y in 0..row.len() {
                b[y][x] = self[x][y];
            }
        }
        return b;
    }
}

impl BoardsMethods for Boards {
    fn contains(&self, board: Board) -> bool {
        for repr in board.all_representations() {
            if self.contains_key(&repr) {
                return true;
            }
        }
        return false;
    }

    fn insert_board(&mut self, b: Board) {
        self.insert(b.representation(), b);
    }
}

fn test() {
    test_rotate();
    test_transpose();
}

fn test_rotate() {
    // XXX    _OX
    // OOO => _OX
    // ___    _OX
    let b: Board = [[X, X, X], [O, O, O], [EMPTY, EMPTY, EMPTY]];
    let rotated: Board = b.rotate();
    assert!(rotated[0] == [EMPTY, O, X]);
    assert!(rotated[1] == [EMPTY, O, X]);
    assert!(rotated[2] == [EMPTY, O, X]);
}

fn test_transpose() {
    // X_O    XO_
    // OX_ => _X_
    // __X    O_X
    let b: Board = [[X, EMPTY, O], [O, X, EMPTY], [EMPTY, EMPTY, X]];
    let transposed: Board = b.transpose();
    assert!(transposed == [[X, O, EMPTY], [EMPTY, X, EMPTY], [O, EMPTY, X]]);
}

Set Up a New Mac (July 2022 Edition)

Updated from three years ago: https://bluebones.net/2019/07/set-up-a-new-mac/

  • Upgrade to latest OSX.
  • Restore home directory from Backblaze. (Or at least dotfiles, .ssh dir, .vim dir, bin directory, ~/Library/Application Support/Alfred 4/*, pwd.dsv.gpg, business, travel)
  • Alfred (and give it Accessibility access, add license key).
  • Dock: remove everything, hiding on, magnification on.
  • Sublime Text 4 (add license key, set "trim_trailing_white_space_on_save": true in sublime prefs, install Package Control, install Solarized Theme).
  • subl commandline sublime.
    $ ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" ~/u/bin/subl
  • sublime-git-ignorer: (1) add Package Control repo https://github.com/apc999/sublime-text-gitignore (2)
    add package sublime-text-gitignore (3) use menu item : File->Exclude Git-ignored (4) Give this a keyboard shortcut in Sublime, Preferences, Key Bindings with { "keys": ["alt+shift+i"], "command": "exclude_ignored" }
  • Install FiraCode and add it to Sublime Text prefs with "font_face": "Fira Code".
  • Hide Desktop icons
    $ defaults write com.apple.finder CreateDesktop false
    $ killall Finder
  • Solarized theme for Terminal and make default in preferences.
  • “Use Option as Meta Key” in Terminal to make left Alt work as Esc.
  • Use Ctrl-f7 or the Keyboard, Shortcuts in System Preferences to give Full Keyboard Access so that tab takes you to every button in a dialog, etc.
  • Homebrew. (Installs OSX commandline tools.)
  • brew install mariadb
  • brew install npm
  • brew install gimme-aws-creds
  • brew install awscli
  • brew install kubectl
  • brew install sops
  • brew install kubectx
  • brew install apache2
  • brew install php
  • Discord.
  • Change all .txt to open with Sublime Text.
  • Show hidden files in Finder. Cmd-Shift-.
  • WhatsApp.
  • 1Password.
  • Chrome. (Log in to Chrome to get uBlock Origin and other extensions.)
  • Skitch.
  • Backblaze.
  • Parallels Desktop + MTGO
  • npm install -g rtm-cli
  • Disable Ask Siri in System Preferences, Siri +Remove Ask Siri from touchbar in System Preferences, Keyboard, Customize Touch Bar…
  • Turn on “tap to click” in System Preferences, Trackpad.
  • Disable long-press (it’s wayyyyy too annoying when selecting text in Chrome): defaults write -g ApplePressAndHoldEnabled -bool false
  • Zoom.
  • Skype.
  • PyCharm (and Solarized theme).
  • GoLand (and Solarized theme).
  • WebStorm (and Solarized theme).
  • Spotify.

Best Guesses in Wordle

My Wordle solver thinks the best first guess is “arise”. After that things diverge pretty quick. Here are the top 20 words used during the 6,397 guesses required to solve all 2,315 words.

arise2315
bludy (when “arise” hits nothing)168
canty (when the “a” in arise hits but out of place)154
denet (when the “e” in arise hits but out of place)121
until (when the “i” hits but out of place)107
hotel (“r” and “e” out of place)100
muton (“s” out of place)80
metal (“e” and “a” out of place)79
plunk (“r” and “i” out of place)78
north (“r” out of place)64
could (“e” in correct place)63
duroy (“a” and “r” out of place)62
malty (“a”, “e” and “r” out of place)61
lanch (“a” and “s” out of place)61
clout (“i” in correct place)51
count (“r” in correct place, and after “bludy” when only the “u” hits)48
blaud (“e” in correct place, “a” in wrong place)47
plant (“i” in correct place, “s” in wrong place)44
spelt (“s” and “e” in wrong place)41
lipid (“i” and “e” in wrong place, or after “until” with “i” in right place)35