I am writing a simple in-memory database. It should support transaction, meaning that BEGIN command creates a new block, the operations in which can be canceled by ROLLBACK command.
I am implementing the list of transaction blocks using a vector. In case of BEGIN a new block is created by push_back()ing an unordered_set of all keys in that transaction block. This is defined as vector<unordered_set<string>> key_in_blocks
What follows is an example of the commands. SET sets value to a variable, GET gets the value of the variable.
BEGIN
SET a 10
GET a       <-- this prints 10
BEGIN
SET a 20
GET a       <-- this prints 20
ROLLBACK
GET a       <-- this prints 10
ROLLBACK    <-- Segmentation Fault here!
So there is a default block at first, and keys_in_block would look like  [()], here () is denoting set, [] denotes vector.
When BEGIN is issued, a new block is created, and SET adds a key a to the block, so keys_in_block = [(), ('a')].
The next BEGIN & SET part is similar, making keys_in_block look like [(), ('a'), ('a')].
Now ROLLBACK undoes operations done in the last block, and keys_in_block should be [(), ('a')], because it is pop_back()ed.
I think my program does what it needs to do up to this point.
On the second ROLLBACK, it throws segmentation fault, and it looks like I cannot even access the keys_in_block variable at all.
Below is my code snippet. It runs inside an infinite while loop, taking commands from user.
} else if (command == "BEGIN") {
    int num_blocks = keys_in_block.size();
    keys_in_block.resize(num_blocks + 1); //new transaction block
} else if (command == "ROLLBACK") {
    if (keys_in_block.size() <= 1) {
        cout << "NO TRANSACTION" << endl;
    }
    else {
        auto &recent_keys = keys_in_block.back();
        // undo operations for keys in the most recent transaction block
        for (const string &key : recent_keys) {
            //...
            //...    a bunch of operations that undoes what's done
            //...    in the most recent transaction block.
            recent_keys.erase(key); // erase all keys in the last entry in `keys_in_block`.
        }
        auto & aa = keys_in_block; //***Just for testing, not relevant to the program.
                                   //This throws Segmentation Fault on second ROLLBACK.
        keys_in_block.pop_back();  //Pop back the last transaction block.
    }
}
In the code snippet, I marked where the segmentation fault is thrown using //***. 
I added that line to because keys_in_block.pop_back() threw segmentation fault, and wanted to see if it throws seg fault just by accessing it.
To me, the algorithm looked absolutely correct, and I couldn't find out what the cause of the problem was.
ROLLBACK doesn't execute when keys_in_block has one block or less, so pop_back() cannot be an issue.
If you need the code for the SET command part, let me know, but I don't think there is a problem in that code.
 
    