History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: COR-1582
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Carl Rosenberger
Reporter: Amphibian Toad@amphibian.dyndns.org
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
db4o Core

Activating a map to depth 1 prevents it from being activated to depth 2

Created: 18/Mar/09 05:54 PM   Updated: 22/Apr/09 03:13 PM
Component/s: None
Affects Version/s: 7.4.84
Fix Version/s: 7.4.91

Time Tracking:
Not Specified

Environment: db4o 7.4.84.12673, 7.4.63.11890 both show this bug (both downloaded from the website at different times)

Peers: Adriano Verona
Order: 3
Iteration: 92
Original IDS Estimate: 1
Resolution Date: 22/Apr/09 03:13 PM
First Response Date: 15/Apr/09 01:02 PM
Labels:
Participants: Amphibian Toad@amphibian.dyndns.org and Carl Rosenberger
Number of Attachments: 0
Number of Comments: 1


 Description  « Hide
Activating a HashMap to depth 1 e.g. by activating the object containing it to depth 2, makes the HashMap empty, and *prevents it from being filled* when activating it to depth 2. This is with more or less default configuration, so translators are being used; from scratch, activation to depth 1 gives a valid but empty HashMap, whereas activating to depth 2 fills in the elements.

So far, not a really nasty bug. But if the HashMap is stored while in this half-activated state, we lose the contents. And thanks to COR-1436, this can happen implicitly, as I have discovered over the last few days: The parent object gets activated to depth 2, hence the HashMap is active to depth 1, i.e. it is empty and cannot be refilled except by deactivating and reactivating. Both the parent object and the HashMap stay in memory for a while, and in a later transaction, we remove the parent object from one arraylist and add it to another, and store() the two arraylists. The empty HashMap then gets stored to disk, leaking its contents. :(

I have a test case, apologies for its length:

HashmapProblemsTest.java:

import java.util.*;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.Configuration;
import freenet.client.async.ManifestElement;
import freenet.support.io.NullBucket;

class HashmapProblemsTest { // aka FCPClient

        HashmapProblemsTest() {
                byIdentifier = new HashMap<String, ClientPutDir>();
                listA = new ArrayList<ClientPutDir>();
                listB = new ArrayList<ClientPutDir>();
        }

        private final Map<String, ClientPutDir> byIdentifier;
        private final List<ClientPutDir> listA;
        private final List<ClientPutDir> listB;

        protected void register(String name, ClientPutDir dir, ObjectContainer container) {
                container.activate(byIdentifier, 2);
                container.activate(listA, 2);
                listA.add(dir);
                dir.storeTo(container);
                container.ext().store(listA, 2);
                byIdentifier.put(name, dir);
                container.ext().store(byIdentifier, 2);
        }

        public static void main(String[] args) {
                Configuration dbConfig = Db4o.newConfiguration();
                dbConfig.freespace().useBTreeSystem();
                dbConfig.messageLevel(1);
                dbConfig.activationDepth(1);
                dbConfig.blockSize(8);
                ObjectContainer db = Db4o.openFile(dbConfig, "hashmap.problems.test.db4o");
                ObjectSet<HashmapProblemsTest> results = db.query(HashmapProblemsTest.class);
                HashmapProblemsTest test = results.hasNext() ? results.next() : null;
                ClientPutDir dir = null;
                if(test != null) {
                        System.err.println("Found HashmapProblemsTest");
                        db.activate(test.byIdentifier, 2);
                        Iterator<String> dirs = test.byIdentifier.keySet().iterator();
                        if(dirs.hasNext()) {
                                String name = dirs.next();
                                System.err.println("Name of ClientPutDir: "+name);
                                dir = test.byIdentifier.get(name);
                        }
                }
                if(dir == null) {
                        System.err.println("No ClientPutDir found. Creating one...");
                        test = new HashmapProblemsTest();
                        db.store(test);
                        db.commit();
                        System.err.println("Creating ClientPutDir...");
                        dir = new ClientPutDir();
                        test.register("test14", dir, db);
                        db.store(dir);
                        db.deactivate(dir, 1);
                        db.commit();
                } else {
                        db.activate(dir, 1);
                        dir.part1(db); // demonstrate the bug
                        // now demonstrate why the bug really kills you
                        test.upgrade(dir, db);
                        // now lets see what's happened to us...
                        dir.part2(db);
                }
        }

        public void upgrade(ClientPutDir dir, ObjectContainer db) {
                // move dir from list A to list B, e.g. because it has completed some work
                listA.remove(dir);
                listB.add(dir);
                db.store(listA);
                db.store(listB);
                db.commit();
        }
}

ClientPutDir.java:

import java.util.*;
import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.Configuration;
import freenet.client.async.ManifestElement;
import freenet.support.io.NullBucket;

class ClientPutDir {
        protected HashMap<String, Object> map;
        private boolean something;

        ClientPutDir() {
                map = new HashMap<String, Object>();
                map.put("name", new ManifestElement("name", "fullname", new NullBucket(), "text/plain", -1));
        }

        void storeTo(ObjectContainer container) {
                container.store(map);
                container.store(this);
        }

        void part1(ObjectContainer container) {
                // First demonstrate the basic bug
                container.activate(this, 2);
                container.activate(map, 2);
                System.err.println("Map size after accidentally activating to depth 1 and then deliberately activating to depth 2: "+map.size());
        }

        void part2(ObjectContainer container) {
                container.activate(map, 2);
                System.err.println("Map size after activating: "+map.size());
                container.deactivate(map, 1);
                container.activate(map, 2);
                System.err.println("Map size after deactivating and activating (real map size): "+map.size());
        }
}


 All   Comments   Work Log   Change History      Sort Order: Ascending order - Click to sort in descending order
Carl Rosenberger - 15/Apr/09 01:02 PM
Using the sources provided the behaviour is reproducible in db4o 7.4
In db4o 7.9 everything works correctly as expected.