jashmenn / activeuuid

Binary uuid keys in Rails
MIT License
340 stars 124 forks source link

Backwards compatibility with integer IDs? #72

Closed threewordphrase closed 9 years ago

threewordphrase commented 9 years ago

Backstory: We have an app that uses a bunch of serialization to store relationships. Converting the entire DB to UUIDs is kind of a non-starter if we can find a way around it. We are moving to UUIDs to support multi-master replication of new records, so we are hoping to maintain support of our old integer IDs.

I just tried a test install of this gem and it (predictably) breaks the old integer IDs. Is there a workaround to make preexisting integer IDs backward compatible, or are we stuck with a string-based strategy?

jashmenn commented 9 years ago

Interesting. More-or-less what you're asking is the point of this library: to provide binary UUIDs under an integer id compatible interface.

Could you post here what errors you're seeing? We've tried as best we can to confirm to the "normal" ActiveRecord conventions to make this possible.

threewordphrase commented 9 years ago

I created a DB migration to convert the ID column to UUID, included ActiveUUID::UUID in my model, restarted webrick and then tried to load a record with an old ID (in this case, 82). Relevant lines from Rails log:

Parameters: {"utf8"=>"✓", "show"=>{"id"=>"82"}, "id"=>"presentation_report"} Show Load (0.5ms) SELECT shows.* FROM shows WHERE shows.id = x'00000000000000000000000000003832' LIMIT 1 Completed 404 Not Found in 98ms (ActiveRecord: 7.2ms) ActiveRecord::RecordNotFound (Couldn't find Show with 'id'=82):

My migration was just this one liner:

change_column :shows, :id, :uuid

IDs were appearing in their integer form when looking at the table in Sequel Pro. Perhaps I missed an installation step?

jashmenn commented 9 years ago

I haven't looked at this closely, but I don't think that a UUID conversion will keep your old IDs. E.g. in that request above, I don't think you'll be able to look up {"id" => "82"} and have that map to the UUID you're expecting.

You might want to try keeping your id column as an integer and then create an additional uuid column. Then re-save each record (which will create a uuid for each one) and then use the uuid column in that way.

dankohn commented 9 years ago

@crypticsymbols Look at using for_each in your migration, which will load in batches of records at about ~1000 at a time but without running out of memory. For each one, you can just replace the existing id with a randomly generated ID from running SecureRandom.uuid.

threewordphrase commented 9 years ago

Well, the trouble is we need to keep the old integer IDs. We can certainly re-save each record, but it doesn't seem to want to take an integer value. After the migration:

2.2.1 :012 > s.id => # 2.2.1 :013 > s.id = 82 => 82 2.2.1 :014 > s.id => nil

Honestly I wasn't really expecting that to work ;). I also tried creating a new UUID column, setting that to an integer on a test, and then querying where uuid = 82 - but still no dice. Do I need to cast or convert it somehow?

Unless there's something obvious I'm missing, I don't want to take up too much time. We can deal with the performance hit of using string PKs.

dankohn commented 9 years ago

Yes, what you're missing is that you can't store an integer in a uuid field. So, it sounds like you'll be best off adding a new column, generating uuids for that column, and then changing your primary key to be the uuid column.

threewordphrase commented 9 years ago

Ah, gotcha. I misinterpreted the first comment about backwards compatibility. Thanks guys!

jashmenn commented 9 years ago

So the problem is that integer ids and binary ids aren't really interchangeable in that sort of "straightforward" way. You can keep your integer ids and uuids by doing the following:

  1. Revert back to your original state where you have your regular ids as integers (e.g. how you had it before you started using activeuuid
  2. Create a migration that adds a uuid column, and make the uuid column the binary uuid.

If you do this, you'll be able to find by id in your old way and then also find by uuid using the new uuids.

In the past, I've even gone so far as to patch .find to auto-detect whether it's an integer id or uuid and then automatically pick the right finder. (I think early versions of this plugin had that. But it isn't 100% reliable so something like that isn't in core.)