среда, 17 апреля 2013 г.

Пост 22. Версионность в Oracle NoSQL Database. Тесты.

Решил я вот вернуться на 2 поста назад и написать еще про версионность. Давайте посмотрим на то как ведет себя Oracle NoSQL база при обработке версионных исключений.
Конкретный пример - инкрементальное увеличение счетчика.
Допустим у нас есть своя социальная сеть. И мы хотим знать сколько пользователей нашей соц. сети существует. Заводим специальный ключ /user/count/ и при добавлении нового пользователя инкрементально увеличиваем его на 1. Достаем старое значение, увеливаем на 1, записаваем. Сказано - сделано. Написали для этого целый класс:

package test;

import java.io.FileNotFoundException;
import java.io.IOException;
import oracle.kv.KVStore;
import CRUD.Create;
import CRUD.Delete;
import Support.OraStore;
import CRUD.Retrieve;
import oracle.kv.Durability;
import oracle.kv.Value;
import oracle.kv.ValueVersion;

public class test_version_badcode {
static String[] hhost = {"localhost:5000"};
static String store = "kvstore";
public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {
OraStore orastore = new OraStore(store, hhost);
KVStore myStore = orastore.getStore();
for (int i = 1; i < 10001; i++) {
ValueVersion vv = CRUD.Retrieve.SelectRowVV("user/count", myStore);
Value v = vv.getValue();
String cntr = new String(v.getValue());
int num_users = Integer.parseInt(cntr) + 1;
CRUD.Create.put("user/count", Integer.toString(num_users), myStore);
}
myStore.close();
System.out.println("Store closed");
}
}


И запустили метод main доторый "добавляет" 10 000 пользователей в 2х разных сессиях, ожидая увидить 20 000 в итоге.

И о "чудо" их 15796... Думаю уже на 25 строчке кода вы подумали что в хорошем обществе так делать не принято. Дейсвительно одна сессия "затирала" результаты другой. Давайте договоримся, что так как показано в примере выше вы делать никогда не будете.
Для решения этой задачи создадим метод который вытаскивает Value и Version по ключу:

public static ValueVersion SelectRowVV(String sKey, KVStore myStore) {

Key myKey = ParseKey.ParseKey(sKey);
ValueVersion vv = null;
try {
vv = myStore.get(myKey);
} catch (RequestTimeoutException re) {
System.out.println(re.getTimeoutMs());
} catch (FaultException fe) {
System.out.println("Unknown error");
} catch (NullPointerException ne) {
System.out.println("Key does not exist");
}
return vv;
}

Так же метод на основе putIfVersion. На вход мы будет передавать версию только что прочитанной пары. Метод будет пытаться сделать Update записи. Если версия поменялась (после чтения другая сессия изменила значение по этому ключу), метод выдаст ошибку (вернет строку состояния операции):
public static String UpdateIfNotchange(String sKey, String data, Version OldVersion, KVStore myStore)
 throws FileNotFoundException, IOException, InterruptedException
{
retdata = null;
Key myKey = ParseKey.ParseKey(sKey);
Value myValue = Value.createValue(data.getBytes());
try {
Version NewVersion = myStore.putIfVersion(myKey, myValue, OldVersion);
if (NewVersion == null) {
System.out.println("Operation Failed! Vesion error");
retdata = "Operation Failed";
} else {
System.out.println("Record created in the kvstore.");
retdata = "Record created in the kvstore";
}
} catch (NullPointerException np) {
System.out.println("KV pair doesn't exist!");
}
return retdata;
}

Имея все это великолепие методов мы можем написать финльный класс, которым протестируем работу версионности в NoSQL (подобно первому случаю):

package test;
import java.io.FileNotFoundException;
import java.io.IOException;
import oracle.kv.KVStore;
import CRUD.Create;
import CRUD.Delete;
import Support.OraStore;
import CRUD.Retrieve;
import oracle.kv.Durability;
import oracle.kv.Value;
import oracle.kv.ValueVersion;

public class test_version {
static String[] hhost = {"localhost:5000"};
static String store = "kvstore";

public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException {

OraStore orastore = new OraStore(store, hhost);
KVStore myStore = orastore.getStore();
String retcode;
for (int i = 1; i < 10001; i++) {
retcode = "Operation Failed";
while (retcode.equals("Operation Failed")) {
ValueVersion vv = CRUD.Retrieve.SelectRowVV("user/count", myStore);
Value v = vv.getValue();
String cntr = new String(v.getValue());
int num_users = Integer.parseInt(cntr) + 1;
retcode = CRUD.Update.UpdateIfNotchange("user/count", Integer.toString(num_users), vv.getVersion(), myStore);
}
}
myStore.close();
System.out.println("Store closed");
}
}

Запускаем 2 параллельные сессии каждая из которых будет добавлять по 10 000 пользователей. Переодически в вывод программы будут падать ошибки:

Operation Failed! Vesion error
Record created in the kvstore.
Record created in the kvstore.
Record created in the kvstore.
Record created in the kvstore.
Operation Failed! Vesion error
Operation Failed! Vesion error
Operation Failed! Vesion error
Operation Failed! Vesion error
Record created in the kvstore.
Record created in the kvstore.

Ничего страшного - после этого exception произойдет повторная попытка чтения update.
В итоге после окончания работы этих 2х методов по ключу /user/count/ мы можем найти ожидаемые 20 000. Ура!

Если будут вопросы - пишите!

Комментариев нет:

Отправить комментарий