zendesk/global_uid — pull request #15 by ggrossman (2014-06-11 05:26:15+00:00)
Title: When no :storage_engine setting is provided, use MyISAM; reset MySQL's auto increment counter when InnoDB is used

In the original article about UID servers that the global_uid gem was based on, the MyISAM engine was used in the example `CREATE TABLE` statement:

http://code.flickr.com/blog/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/

I think this was for a good reason... InnoDB behaves strangely with respect to auto_increment columns and `UPDATE` queries, which are used in global_uid to obtain many IDs at once in `get_many_uids_for_class`.

Example:

```
2327-ggrossman:global_uid ggrossman$ mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7523
Server version: 5.5.20 Source distribution

Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> \u test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> create table t (id bigint(21) auto_increment, stub char(1), primary key (`id`), unique key `stub` (`stub`)) engine=InnoDB;
Query OK, 0 rows affected (0.02 sec)

mysql> replace into t (stub) values ('a');
Query OK, 1 row affected (0.01 sec)

mysql> select id from t;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)

mysql> update t set id=id+100 where stub='a';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select id from t;
+-----+
| id |
+-----+
| 101 |
+-----+
1 row in set (0.00 sec)

mysql> replace into t (stub) values ('a');
Query OK, 2 rows affected (0.01 sec)

mysql> select id from t;
+----+
| id |
+----+
| 2 |
+----+
1 row in set (0.00 sec)

mysql>
```

However, if `ALTER TABLE t AUTO_INCREMENT=1` is issued after the `UPDATE` query, the ID column behaves as expected.

```
mysql> update t set id=id+100;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select id from t;
+-----+
| id |
+-----+
| 102 |
+-----+
1 row in set (0.00 sec)

mysql> alter table t auto_increment=1;
Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0

mysql> replace into t (stub) values ('a');
Query OK, 2 rows affected (0.01 sec)

mysql> select id from t;
+-----+
| id |
+-----+
| 103 |
+-----+
1 row in set (0.00 sec)

mysql>
```

### Solution

The solution I propose here is to default the MySQL storage engine to MyISAM if the `:storage_engine` setting is not provided in `global_uid_options`. To this point, global_uid has defaulted to the storage engine setting of the MySQL server, which could very well be InnoDB.

The second part of the fix is to issue `ALTER TABLE table_name AUTO_INCREMENT=1` after doing an `UPDATE` query if the InnoDB engine is being used. This `ALTER TABLE` command is sufficient to force MySQL to recalculate the auto increment counter. It doesn't actually set it to 1; it sets it to the maximum value in the auto increment column. This `ALTER TABLE` command appears to be harmless for MyISAM as well, but I've made it only happen for InnoDB to reduce risk.

/cc @eac @grosser @osheroff @vkmita @rtatsumi

### References
- None

### Risks
- Because we're only issuing the `ALTER TABLE` command if `global_uid_options` says the storage engine is InnoDB, there is a risk that the actual uid tables aren't using the correct MySQL storage engine, so that should be verified by the DBA.