Środa ok. godz. 16:30. Pracuję nad poprawką obsługi błędów dotyczącą naszego rozproszonego systemu plików. "No, to jeszcze zapuszczę testy, sprawdzę logi i jeśli wszystko ok. to commit i koniec roboty na dzisiaj". A tam w logach widzę takie coś:
Kopiuj
... Block not found: java.nio.HeapByteBuffer[pos=13748 lim=13764 cap=5498210]
Hmm, ta informacja nie wygląda na przydatną. Popatrzmy skąd to leci:
Kopiuj
ByteBuffer blockId = ....
...
logger.error("Block not found: " + blockId);
No tak, Cassandra wszystko traktuje jako ByteBuffer. Znacznie bardziej przydatne byłoby zalogować zawartość bufora oznaczającą id tego brakującego bloku niż te bzdury co wypisuje ByteBuffer.toString(). No, ale wiedząc, że w tym buforze jest UUID, to przecież 5 minut roboty? Cassandra ma wewnętrzny typ UUID, do konwersji z ByteBuffer na UUID i z powrotem (zresztą są takie konwersje dla wszystkich wspieranych typów, włączając kolekcje), więc to powinien być pikuś.
Kopiuj
logger.error("Block not found: " + UUIDType.instance.compose(blockId));
No, jest chyba dobrze:
Kopiuj
Block not found: 39653631-6266-3130-3066-666631316533
Zadowolony już miałem commitować, kiedy zorientowałem się, że to nie jest ten UUID, który miał być. Tzn. nie jest ten, który był po stronie klienta (klient wyszukuje blok po id). WTF?
Po 15 minutach nieskutecznego szukania co może być nie tak, postanowiłem zapytać kolegów od Cassandry co robię źle, że mi się UUID psuje. Dostałem odpowiedź, żebym spróbował tego:
Kopiuj
logger.error("Block not found: " + UUIDGen.getUUID(blockId));
Niestety, znowu nieprawidłowy UUID.
- Jakiego typu masz ten UUID? Type 1 czy Type 3?
- Żebym to ja wiedział... ....(po kolejnych kilkunastu minutach szukania w kodzie, bo oczywiście nie ja to pisałem)... Type 1 (TimeUUID)
- No to UUIDGen.getUUID musi działać.
Damn. Ale nie działa. Ponieważ getUUID raczej jest napisane dobrze, doszedłem do wniosku, że to co zostało wrzucone do bufora musiało zostać poddane jeszcze jakiejś dodatkowej obróbce. Znajdźmy to miejsce, gdzie oni to serializują do bufora. Znowu po jakimś długim czasie odkryłem to:
Kopiuj
ByteBuffer uuidToByteBuffer(UUID id)
{
return ByteBufferUtil.bytes(Hex.bytesToHex(UUIDGen.decompose(id)));
}
Spoko, powinna być tu gdzieś też odwrotna funkcja. Ale nie, nigdzie nie ma. Dobra, no to spróbujmy ją odwrócić:
Kopiuj
UUID byteBufferToUUID(ByteBuffer bb)
{
return ?????? (Hex.hexToBytes(ByteBufferUtil.string(bb));
}
Pozostało znalezienie jak z byte[] zrobić UUID i wstawić to w ????. Może UUIDGen ma metodę compose, odwrotną do decompose. F***ck. Nie ma. O! ale jest UUID.nameUUIDFromBytes() i chyba pasuje.
Kopiuj
UUID byteBufferToUUID(ByteBuffer bb)
{
return UUID.nameUUIDFromBytes(Hex.hexToBytes(ByteBufferUtil.string(bb));
}
No, teraz powinno być dobrze. Odpalam i.... znowu nic z tego. Jeszcze inny UUID dostałem. UUID.nameUUIDFromBytes działa bowiem dla UUIDów typu 3 a nie 1.
....
W końcu okazało się, że rozwiązaniem jest:
Kopiuj
UUID byteBufferToUUID(ByteBuffer bb)
{
return UUIDGen.getUUID(ByteBuffer.wrap(Hex.hexToBytes(ByteBufferUtil.string(bb));
}
Uff. Patrzę na zegarek: 19:40. A miało być 5 minut ;)
W jakim celu autor kodu zdecydował się w taki pokrętny sposób serializować UUIDy (zamiast wykorzystać UUIDType compose/decompose) to niestety się nie dowiedziałem i chyba nigdy nie dowiem, bo ten gość już nie pracuje.