Warning: session_start(): open(/var/lib/php/sessions/sess_kt7n39uqhpt5u53hggs3p282v3, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
// A concept should be an object, not just a string.
// Functions that should always be there for child processes:
please include function dbLock.
static int concepts_internStringsLongerThan = 10;
static new ThreadLocal concepts_unlisted;
static interface Derefable {
Concept get();
}
static interface IConceptIndex {
void update(Concept c); // also for adding
void remove(Concept c);
}
static interface IFieldIndex {
Cl getAll(Val val);
L allValues(); // returns a cloned list
MultiSet allValues_multiSet();
}
sclass Concepts {
Map concepts = synchroTreeMap();
new HashMap perClassData;
Map miscMap;
// set to "-" for non-persistent (possibly not implemented)
// also, can include a case ID ("#123/1")
// TODO: have an actual directory instead
S programID;
long idCounter;
volatile long changes, changesWritten, lastChange;
volatile java.util.Timer autoSaver;
volatile bool savingConcepts, dontSave, noXFullGrab;
bool vmBusSend = true;
bool initialSave = true; // set to false to avoid initial useless saving
int autoSaveInterval = -1000; // 1 second + wait logic
bool useGZIP = true, quietSave;
ReentrantLock lock = new ReentrantLock(true);
ReentrantLock saverLock = new ReentrantLock(true);
long lastSaveTook, lastSaveWas;
float maxAutoSavePercentage = 10;
L conceptIndices;
Map, Map> fieldIndices;
Map, Map> ciFieldIndices;
L saveActions = synchroList();
O classFinder = _defaultClassFinder();
L onAllChanged = synchroList(); // list of runnables
transient O saveWrapper; // VF1, to profile saving
*() {}
*(S *programID) {}
synchronized long internalID() {
do {
++idCounter;
} while (hasConcept(idCounter));
ret idCounter;
}
void initProgramID() {
if (programID == null)
programID = getDBProgramID();
}
// Now tries to load from bot first, then go to disk.
Concepts load() {
ret load(false);
}
Concepts safeLoad() {
ret load(true);
}
Concepts load(bool allDynamic) {
initProgramID();
try {
if (tryToGrab(allDynamic)) ret this;
} catch e {
if (!exceptionMessageContains(e, "no xfullgrab"))
printShortException(e);
print("xfullgrab failed - loading DB of " + programID + " from disk");
}
ret loadFromDisk(allDynamic);
}
Concepts loadFromDisk() { ret loadFromDisk(false); }
Concepts loadFromDisk(bool allDynamic) {
if (nempty(concepts)) clearConcepts();
//DynamicObject_loading.set(true); // now done in unstructure()
//try {
// minimal crash recovery
restoreLatestBackupIfConceptsFileEmpty(programID, doIt := true);
long time = now();
Map _concepts = concepts; // empty map
readLocally2_allDynamic.set(allDynamic);
temp tempSetTL(readLocally2_classFinder, classFinder);
readLocally2(this, programID, "concepts");
Map __concepts = concepts;
concepts = _concepts;
concepts.putAll(__concepts);
int l = readLocally_stringLength;
int tokrefs = unstructure_tokrefs;
assignConceptsToUs();
done("Loaded " + n(l(concepts), "concepts"), time);
if (fileSize(getProgramFile(programID, "idCounter.structure")) != 0)
readLocally2(this, programID, "idCounter");
else
calcIdCounter();
/*} finally {
DynamicObject_loading.set(null);
}*/
if (initialSave) allChanged();
ret this;
}
Concepts loadConcepts() { ret load(); }
bool tryToGrab(bool allDynamic) {
if (sameSnippetID(programID, getDBProgramID())) false;
temp RemoteDB db = connectToDBOpt(programID);
if (db != null) {
loadGrab(db.fullgrab(), allDynamic);
true;
}
false;
}
Concepts load(S grab) {
ret loadGrab(grab, false);
}
Concepts safeLoad(S grab) {
ret loadGrab(grab, true);
}
Concepts loadGrab(S grab, bool allDynamic) {
clearConcepts();
DynamicObject_loading.set(true);
try {
Map map = (Map) unstructure(grab, allDynamic, classFinder);
concepts.putAll(map);
assignConceptsToUs();
for (long l : map.keySet())
idCounter = max(idCounter, l);
} finally {
DynamicObject_loading.set(null);
}
allChanged();
ret this;
}
void assignConceptsToUs() {
for (Concept c : values(concepts)) c._concepts = this;
for (Concept c : values(concepts))
callOpt_noArgs(c, "_doneLoading2"); // doneLoading2 is called on all concepts after all concepts are loaded
}
S progID() {
ret programID == null ? getDBProgramID() : programID;
}
Concept getConcept(S id) {
ret empty(id) ? null : getConcept(parseLong(id));
}
Concept getConcept(long id) {
ret (Concept) concepts.get((long) id);
}
Concept getConcept(RC ref) {
ret ref == null ? null : getConcept(ref.longID());
}
bool hasConcept(long id) {
ret concepts.containsKey((long) id);
}
void deleteConcept(long id) {
Concept c = getConcept(id);
if (c == null)
print("Concept " + id + " not found");
else
c.delete();
}
void calcIdCounter() {
long id_ = 0;
for (long id : keys(concepts))
id_ = max(id_, id);
idCounter = id_+1;
saveLocally2(this, programID, "idCounter");
}
void saveConceptsIfDirty() { saveConcepts(); }
void save() { saveConcepts(); }
void saveConcepts() {
if (dontSave) ret;
initProgramID();
saverLock.lock();
savingConcepts = true;
long start = now(), time;
try {
S s = null;
//synchronized(main.class) {
long _changes = changes;
if (_changes == changesWritten) ret;
final File f = getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure");
lock.lock();
long fullTime = now();
try {
saveLocally2(this, programID, "idCounter");
if (useGZIP) {
callRunnableWithWrapper(saveWrapper, r {
saveGZStructureToFile(f, cloneMap(concepts));
});
getProgramFile(programID, "concepts.structure").delete();
} else
s = fullStructure();
} finally {
lock.unlock();
}
while (nempty(saveActions))
pcallF(popFirst(saveActions));
changesWritten = _changes; // only update when structure didn't fail
if (!useGZIP) {
time = now()-start;
if (!quietSave)
print("Saving " + toM(l(s)) + "M chars (" /*+ changesWritten + ", "*/ + time + " ms)");
start = now();
saveTextFile(f, javaTokWordWrap(s));
getProgramFile(programID, "concepts.structure.gz").delete();
}
copyFile(f, getProgramFile(programID, "backups/concepts.structure" + (useGZIP ? ".gz" : "") + ".backup" + ymd() + "-" + formatInt(hours(), 2)));
time = now()-start;
if (!quietSave)
print(programID + ": Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)");
lastSaveWas = fullTime;
lastSaveTook = now()-fullTime;
} finally {
savingConcepts = false;
saverLock.unlock();
}
}
void _autoSaveConcepts() {
if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) {
long pivotTime = Math.round(lastSaveWas+lastSaveTook*100.0/maxAutoSavePercentage);
if (now() < pivotTime) {
//print("Skipping auto-save (last save took " + lastSaveTook + ")");
ret;
}
}
try {
saveConcepts();
} catch e {
print("Concept save failed, will try again: " + e);
}
}
S fullStructure() {
ret structure(cloneMap(concepts));
}
void clearConcepts() {
concepts.clear();
allChanged();
}
void allChanged() {
synchronized(this) { ++changes; lastChange = sysNow(); }
if (vmBusSend) vmBus_send('conceptsChanged, this);
pcallFAll(onAllChanged);
}
// auto-save every second if dirty
synchronized void autoSaveConcepts() {
if (autoSaver == null) {
if (isTransient()) fail("Can't persist transient database");
autoSaver = doEvery_daemon(abs(autoSaveInterval), r { _autoSaveConcepts() });
// print("Installed auto-saver (" + autoSaveInterval + " ms, " + progID() + ")");
}
}
void cleanMeUp() {
bool shouldSave = autoSaver != null;
if (autoSaver != null) {
autoSaver.cancel();
autoSaver = null;
}
while (savingConcepts) sleepInCleanUp(10);
if (shouldSave)
saveConceptsIfDirty();
}
Map getIDsAndNames() {
new Map map;
Map cloned = cloneMap(concepts);
for (long id : keys(cloned))
map.put(id, cloned.get(id).className);
ret map;
}
void deleteConcepts(L l) {
if (l != null) for (O o : cloneList(l))
if (o instanceof Long) {
Concept c = concepts.get(o);
if (c != null) c.delete();
} else if (o cast Concept)
o.delete();
else
warn("Can't delete " + getClassName(o));
}
A conceptOfType(Class type) {
IConceptCounter counter = conceptCounterForClass(type);
if (counter != null) ret (A) first(counter.allConcepts());
ret firstOfType(allConcepts(), type);
}
L conceptsOfType(Class type) {
IConceptCounter counter = conceptCounterForClass(type);
if (counter != null) ret (L) cloneList(counter.allConcepts());
ret filterByType(allConcepts(), type);
}
L listConcepts(Class type) {
ret conceptsOfType(type);
}
L list(Class type) {
ret conceptsOfType(type);
}
// TODO: would be better to make this Cl (indices may return sets)
L list(S type) {
ret conceptsOfType(type);
}
L conceptsOfType(S type) {
ret filterByDynamicType(allConcepts(), "main$" + type);
}
bool hasConceptOfType(Class extends Concept> type) {
ret hasType(allConcepts(), type);
}
void persistConcepts() {
loadConcepts();
autoSaveConcepts();
}
// We love synonyms
void conceptPersistence() { persistConcepts(); }
Concepts persist() { persistConcepts(); ret this; }
void persist(Int interval) {
if (interval != null) autoSaveInterval = interval;
persist();
}
// Runs r if there is no concept of that type
A ensureHas(Class c, Runnable r) {
A a = conceptOfType(c);
if (a == null) {
r.run();
a = conceptOfType(c);
if (a == null)
fail("Concept not made by " + r + ": " + shortClassName(c));
}
ret a;
}
// Ensures that every concept of type c1 is ref'd by a concept of
// type c2.
// Type of func: voidfunc(concept)
void ensureHas(Class extends Concept> c1, Class extends Concept> c2, O func) {
for (Concept a : conceptsOfType(c1)) {
Concept b = findBackRef(a, c2);
if (b == null) {
callF(func, a);
b = findBackRef(a, c2);
if (b == null)
fail("Concept not made by " + func + ": " + shortClassName(c2));
}
}
}
// Type of func: voidfunc(concept)
void forEvery(Class extends Concept> type, O func) {
for (Concept c : conceptsOfType(type))
callF(func, c);
}
int deleteAll(Class extends Concept> type) {
L l = (L) conceptsOfType(type);
for (Concept c : l) c.delete();
ret l(l);
}
Collection allConcepts() {
synchronized(concepts) {
ret new L(values(concepts));
}
}
IConceptCounter conceptCounterForClass(Class extends Concept> c) {
for (IFieldIndex idx : values(mapGet(fieldIndices, c)))
if (idx cast IConceptCounter) ret idx;
for (IFieldIndex idx : values(mapGet(ciFieldIndices, c)))
if (idx cast IConceptCounter) ret idx;
null;
}
int countConcepts(Class c, O... params) {
if (empty(params)) {
IConceptCounter counter = conceptCounterForClass(c);
if (counter != null) ret counter.countConcepts();
ret l(list(c));
}
int n = 0;
for (A x : list(c)) if (checkConceptFields(x, params)) ++n;
ret n;
}
int countConcepts(S c, O... params) {
if (empty(params)) ret l(list(c));
int n = 0;
for (Concept x : list(c)) if (checkConceptFields(x, params)) ++n;
ret n;
}
int countConcepts() {
ret l(concepts);
}
synchronized void addConceptIndex(IConceptIndex index) {
if (conceptIndices == null)
conceptIndices = new L;
conceptIndices.add(index);
}
synchronized void removeConceptIndex(IConceptIndex index) {
if (conceptIndices == null) ret;
conceptIndices.remove(index);
if (empty(conceptIndices)) conceptIndices = null;
}
synchronized void addFieldIndex(Class extends Concept> c, S field, IFieldIndex index) {
if (fieldIndices == null)
fieldIndices = new HashMap;
Map map = fieldIndices.get(c);
if (map == null)
fieldIndices.put(c, map = new HashMap);
map.put(field, index);
}
synchronized IFieldIndex getFieldIndex(Class extends Concept> c, S field) {
if (fieldIndices == null) null;
Map map = fieldIndices.get(c);
ret map == null ? null : map.get(field);
}
synchronized void addCIFieldIndex(Class extends Concept> c, S field, IFieldIndex index) {
if (ciFieldIndices == null)
ciFieldIndices = new HashMap;
Map map = ciFieldIndices.get(c);
if (map == null)
ciFieldIndices.put(c, map = new HashMap);
map.put(field, index);
}
synchronized IFieldIndex getCIFieldIndex(Class extends Concept> c, S field) {
if (ciFieldIndices == null) null;
Map map = ciFieldIndices.get(c);
ret map == null ? null : map.get(field);
}
// inter-process methods
RC xnew(S name, O... values) {
ret new RC(cnew(name, values));
}
void xset(long id, S field, O value) {
xset(new RC(id), field, value);
}
void xset(RC c, S field, O value) {
if (value instanceof RC)
value = getConcept((RC) value);
cset(getConcept(c), field, value);
}
O xget(long id, S field) {
ret xget(new RC(id), field);
}
O xget(RC c, S field) {
ret xgetPost(cget(getConcept(c), field));
}
O xgetPost(O o) {
o = deref(o);
if (o instanceof Concept)
ret new RC((Concept) o);
ret o;
}
void xdelete(long id) {
xdelete(new RC(id));
}
void xdelete(RC c) {
getConcept(c).delete();
}
void xdelete(L l) {
for (RC c : l)
xdelete(c);
}
L xlist() {
ret map("toPassRef", allConcepts());
}
L xlist(S className) {
ret map("toPassRef", conceptsOfType(className));
}
bool isTransient() { ret eq(programID, "-"); }
S xfullgrab() {
if (noXFullGrab) fail("no xfullgrab (DB too large)");
lock lock();
if (changes == changesWritten && !isTransient())
ret loadConceptsStructure(programID);
ret fullStructure();
}
/* dev.
Either xfullgrabGZipped() {
lock lock();
if (changes == changesWritten && !isTransient())
ret loadConceptsStructure(programID);
ret fullStructure();
}*/
void xshutdown() {
// Killing whole VM if someone wants this DB to shut down
cleanKillVM();
}
long xchangeCount() { ret changes; }
int xcount() { ret countConcepts(); }
void register(Concept c) {
if (c._concepts == this) ret;
if (c._concepts != null) fail("Can't re-register");
c._concepts = this;
c.id = internalID();
c.created = now();
concepts.put((long) c.id, c);
c.change();
}
void conceptChanged(Concept c) {
allChanged();
if (conceptIndices != null)
for (IConceptIndex index : conceptIndices)
index.update(c);
}
bool hasUnsavedData() {
ret changes != changesWritten || savingConcepts;
}
} // end of Concepts
sclass Concept extends DynamicObject {
transient Concepts _concepts; // Where we belong
long id;
long created, _modified;
L[ refs;
L][ backRefs;
// used only internally (cnew)
*(S className) {
super(className);
_created();
}
*() {
if (!_loading()) {
//className = shortClassName(this); // XXX - necessary?
//print("New concept of type " + className);
_created();
}
}
*(bool unlisted) {
if (!unlisted) _created();
}
static bool loading() { ret _loading(); }
static bool _loading() { ret dynamicObjectIsLoading(); }
void _created() {
if (!isTrue(concepts_unlisted!))
db_mainConcepts().register(this);
}
/*void put(S field, O value) {
fieldValues.put(field, value);
change();
}
O get(S field) {
ret fieldValues.get(field);
}*/
class Ref {
A value;
*() {
if (!dynamicObjectIsLoading()) refs = addDyn(refs, this);
}
*(A *value) {
refs = addDyn(refs, this);
index();
}
// get owning concept (source)
Concept concept() {
ret Concept.this;
}
// get target
A get() { ret value; }
bool has() { ret value != null; }
void set(A a) {
if (a == value) ret;
unindex();
value = a;
index();
}
void set(Ref ref) { set(ref.get()); }
void clear() { set((A) null); }
// TODO: sync all the indexing and unindexing!?
void index() {
if (value != null)
value._addBackRef(this);
change();
}
void unindex() {
if (value != null)
value._removeBackRef(this);
}
void change() {
Concept.this.change();
}
}
class RefL extends AbstractList {
new L][> l;
public A set(int i, A o) {
A prev = l.get(i).get();
l.get(i).set(o);
ret prev;
}
public void add(int i, A o) {
l.add(i, new Ref(o));
}
public A get(int i) {
ret l.get(i).get();
}
public A remove(int i) {
ret l.remove(i).get();
}
public int size() {
ret l.size();
}
public bool contains(O o) {
if (o instanceof Concept)
for (Ref r : l) if (eq(r!, o)) true;
ret super.contains(o);
}
}
void delete() {
//name = "[defunct " + name + "]";
//defunct = true;
//energy = 0;
// clean refs
for (Ref r : unnull(refs))
r.unindex();
refs = null;
// set back refs to null
for (Ref r : cloneList(backRefs))
r.set((Concept) null);
backRefs = null;
if (_concepts != null) {
_concepts.concepts.remove((long) id);
_concepts.allChanged();
if (_concepts.conceptIndices != null)
for (IConceptIndex index : _concepts.conceptIndices)
index.remove(this);
_concepts = null;
}
id = 0;
}
BaseXRef export() {
ret new BaseXRef(_concepts.progID(), id);
}
// notice system of a change in this object
void change() {
_modified = now();
_change_withoutUpdatingModifiedField();
}
void _change_withoutUpdatingModifiedField() {
if (_concepts != null) _concepts.conceptChanged(this);
}
void _change() { change(); }
S _programID() {
ret _concepts == null ? getDBProgramID() : _concepts.progID();
}
// overridable
void _addBackRef(Concept.Ref ref) {
backRefs = addDyn(backRefs, ref);
}
void _removeBackRef(Concept.Ref ref) {
backRefs = removeDyn(backRefs, ref);
}
// convenience methods
void _setField(S field, O value) {
cset(this, field, value);
}
void _setFields(O... values) {
cset(this, values);
}
Concepts concepts() { ret _concepts; }
} // end of Concept
// remote reference (for inter-process communication or
// external databases). Formerly "PassRef".
// prepared for string ids if we do them later
sclass RC {
transient O owner;
S id;
*() {} // make serialisation happy
*(long id) { this.id = str(id); }
*(O owner, long id) { this.id = str(id); this.owner = owner; }
*(Concept c) { this(c.id); }
long longID() { ret parseLong(id); }
public S toString() {
ret id;
}
}
// Reference to a concept in another program
sclass BaseXRef {
S programID;
long id;
*() {}
*(S *programID, long *id) {}
public bool equals(O o) {
if (!(o instanceof BaseXRef)) false;
BaseXRef r = cast o;
ret eq(programID, r.programID) && eq(id, r.id);
}
public int hashCode() {
ret programID.hashCode() + (int) id;
}
}
// BaseXRef as a concept
sclass XRef extends Concept {
BaseXRef ref;
*() {}
*(BaseXRef *ref) { _doneLoading2(); }
// after we have been added to concepts
void _doneLoading2() {
getIndex().put(ref, this);
}
HashMap getIndex() {
ret getXRefIndex(_concepts);
}
}
static synchronized HashMap getXRefIndex(Concepts concepts) {
HashMap cache = (HashMap) concepts.perClassData.get(XRef.class);
if (cache == null)
concepts.perClassData.put(XRef.class, cache = new HashMap);
ret cache;
}
// uses mainConcepts
static XRef lookupOrCreateXRef(BaseXRef ref) {
XRef xref = getXRefIndex(db_mainConcepts()).get(ref);
if (xref == null)
xref = new XRef(ref);
ret xref;
}
// define standard concept functions to use main concepts
static void cleanMeUp_concepts() {
if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp();
// mainConcepts = null; // TODO
}
svoid loadAndAutoSaveConcepts {
db_mainConcepts().persist();
}
svoid loadAndAutoSaveConcepts(int interval) {
db_mainConcepts().persist(interval);
}
static RC toPassRef(Concept c) {
ret new RC(c);
}
// so we can instantiate the program to run as a bare DB bot
please include function bareDBMode.]