#include "copyright.h" #include "config.h" #include "params.h" #include "db.h" #include "tune.h" #include "props.h" #include "externs.h" #include "interface.h" #ifdef DISKBASE extern FILE *input_file; extern FILE *delta_infile; extern FILE *delta_outfile; extern void getproperties(FILE * f, dbref obj, const char *pdir); long propcache_hits = 0L; long propcache_misses = 0L; struct pload_Q { dbref obj; long count; int Qtype; }; struct pload_Q propchanged_Q = { NOTHING, 0, PROPS_CHANGED }; struct pload_Q proploaded_Q = { NOTHING, 0, PROPS_LOADED }; struct pload_Q proppri_Q = { NOTHING, 0, PROPS_PRIORITY }; void removeobj_ringqueue(dbref obj) { struct pload_Q *ref = NULL; switch (DBFETCH(obj)->propsmode) { case PROPS_UNLOADED: return; break; case PROPS_LOADED: ref = &proploaded_Q; break; case PROPS_PRIORITY: ref = &proppri_Q; break; case PROPS_CHANGED: ref = &propchanged_Q; break; } if (DBFETCH(obj)->nextold == NOTHING || DBFETCH(obj)->prevold == NOTHING) return; if (!ref || ref->obj == NOTHING) return; if (DBFETCH(obj)->nextold == obj || DBFETCH(obj)->prevold == obj) { if (ref->obj == obj) ref->obj = NOTHING; } else { DBFETCH(DBFETCH(obj)->prevold)->nextold = DBFETCH(obj)->nextold; DBFETCH(DBFETCH(obj)->nextold)->prevold = DBFETCH(obj)->prevold; if (ref->obj == obj) ref->obj = DBFETCH(obj)->nextold; } DBFETCH(obj)->prevold = NOTHING; DBFETCH(obj)->nextold = NOTHING; ref->count--; } void addobject_ringqueue(dbref obj, int mode) { struct pload_Q *ref = NULL; removeobj_ringqueue(obj); DBFETCH(obj)->propsmode = mode; switch (mode) { case PROPS_UNLOADED: DBFETCH(obj)->nextold = NOTHING; DBFETCH(obj)->prevold = NOTHING; return; break; case PROPS_LOADED: ref = &proploaded_Q; break; case PROPS_PRIORITY: ref = &proppri_Q; break; case PROPS_CHANGED: ref = &propchanged_Q; break; } if (ref->obj == NOTHING) { DBFETCH(obj)->nextold = obj; DBFETCH(obj)->prevold = obj; ref->obj = obj; } else { DBFETCH(obj)->nextold = ref->obj; DBFETCH(DBFETCH(ref->obj)->prevold)->nextold = obj; DBFETCH(obj)->prevold = DBFETCH(ref->obj)->prevold; DBFETCH(ref->obj)->prevold = obj; } ref->count++; } dbref first_ringqueue_obj(struct pload_Q *ref) { if (!ref) return NOTHING; return (ref->obj); } dbref next_ringqueue_obj(struct pload_Q * ref, dbref obj) { if (DBFETCH(obj)->nextold == ref->obj) return NOTHING; return (DBFETCH(obj)->nextold); } #define FETCHSTATS_INTERVAL1 120 #define FETCHSTATS_INTERVAL2 600 #define FETCHSTATS_INTERVAL3 3600 #define FETCHSTATS_SLOT_TIME 30 #define FETCHSTATS_SLOTS ((FETCHSTATS_INTERVAL3 / FETCHSTATS_SLOT_TIME) + 1) static int fetchstats[FETCHSTATS_SLOTS]; static int lastfetchslot = -1; void update_fetchstats(void) { time_t now; int slot, i; now = time(NULL); slot = ((now / FETCHSTATS_SLOT_TIME) % FETCHSTATS_SLOTS); if (slot != lastfetchslot) { if (lastfetchslot == -1) { for (i = 0; i < FETCHSTATS_SLOTS; i++) { fetchstats[i] = -1; } } while (lastfetchslot != slot) { lastfetchslot = ((lastfetchslot + 1) % FETCHSTATS_SLOTS); fetchstats[lastfetchslot] = 0; } } fetchstats[slot] += 1; } void report_fetchstats(dbref player) { int i, count, slot; double sum, minv, maxv; char buf[BUFFER_LEN]; time_t now; now = time(NULL); i = slot = ((now / FETCHSTATS_SLOT_TIME) % FETCHSTATS_SLOTS); while (lastfetchslot != slot) { lastfetchslot = ((lastfetchslot + 1) % FETCHSTATS_SLOTS); fetchstats[lastfetchslot] = 0; } sum = maxv = 0.0; minv = fetchstats[i]; count = 0; for (; count < (FETCHSTATS_INTERVAL1 / FETCHSTATS_SLOT_TIME); count++) { if (fetchstats[i] == -1) break; sum += fetchstats[i]; if (fetchstats[i] < minv) minv = fetchstats[i]; if (fetchstats[i] > maxv) maxv = fetchstats[i]; i = ((i + FETCHSTATS_SLOTS - 1) % FETCHSTATS_SLOTS); } snprintf(buf, sizeof(buf), "Disk Fetches %2g minute min/ave/max: %.2f/%.2f/%.2f", (FETCHSTATS_INTERVAL1 / 60.0), (minv * 60.0 / FETCHSTATS_SLOT_TIME), (sum * 60.0 / (FETCHSTATS_SLOT_TIME * count)), (maxv * 60.0 / FETCHSTATS_SLOT_TIME)); notify(player, buf); for (; count < (FETCHSTATS_INTERVAL2 / FETCHSTATS_SLOT_TIME); count++) { if (fetchstats[i] == -1) break; sum += fetchstats[i]; if (fetchstats[i] < minv) minv = fetchstats[i]; if (fetchstats[i] > maxv) maxv = fetchstats[i]; i = ((i + FETCHSTATS_SLOTS - 1) % FETCHSTATS_SLOTS); } snprintf(buf, sizeof(buf), "Disk Fetches %2g minute min/ave/max: %.2f/%.2f/%.2f", (FETCHSTATS_INTERVAL2 / 60.0), (minv * 60.0 / FETCHSTATS_SLOT_TIME), (sum * 60.0 / (FETCHSTATS_SLOT_TIME * count)), (maxv * 60.0 / FETCHSTATS_SLOT_TIME)); notify(player, buf); for (; count < (FETCHSTATS_INTERVAL3 / FETCHSTATS_SLOT_TIME); count++) { if (fetchstats[i] == -1) break; sum += fetchstats[i]; if (fetchstats[i] < minv) minv = fetchstats[i]; if (fetchstats[i] > maxv) maxv = fetchstats[i]; i = ((i + FETCHSTATS_SLOTS - 1) % FETCHSTATS_SLOTS); } snprintf(buf, sizeof(buf), "Disk Fetches %2g minute min/ave/max: %.2f/%.2f/%.2f", (FETCHSTATS_INTERVAL3 / 60.0), (minv * 60.0 / FETCHSTATS_SLOT_TIME), (sum * 60.0 / (FETCHSTATS_SLOT_TIME * count)), (maxv * 60.0 / FETCHSTATS_SLOT_TIME)); notify(player, buf); } void report_cachestats(dbref player) { dbref obj; int count, total, checked, gap, ipct; time_t when, now; double pct; char buf[BUFFER_LEN]; notify(player, "LRU proploaded cache time distribution graph."); total = proploaded_Q.count; checked = 0; gap = 0; when = now = time(NULL); notify(player, "Mins Objs (%of db) Graph of #objs vs. age."); for (; checked < total; when -= 60) { count = 0; obj = first_ringqueue_obj(&proploaded_Q); while (obj != NOTHING) { if (DBFETCH(obj)->propstime > (when - 60) && DBFETCH(obj)->propstime <= when) count++; obj = next_ringqueue_obj(&proploaded_Q, obj); } checked += count; pct = count * 100.0 / total; ipct = count * 100 / total; if (ipct > 50) ipct = 50; if (count) { if (gap) notify(player, "[gap]"); snprintf(buf, sizeof(buf), "%3ld:%6d (%5.2f%%) %*s", ((now - when) / 60), count, pct, ipct, "*"); notify(player, buf); gap = 0; } else { gap = 1; } } } void diskbase_debug(dbref player) { char buf[BUFFER_LEN]; double ph, pm; ph = propcache_hits; pm = propcache_misses; snprintf(buf, sizeof(buf), "Propcache hit ratio: %.3f%% (%ld hits / %ld fetches)", (100.0 * ph / (ph + pm)), propcache_hits, propcache_misses); notify(player, buf); report_fetchstats(player); notify_fmt(player, "PropLoaded count: %d", proploaded_Q.count); notify_fmt(player, "PropPriority count: %d", proppri_Q.count); notify_fmt(player, "PropChanged count: %d", propchanged_Q.count); report_cachestats(player); } void unloadprops_with_prejudice(dbref obj) { PropPtr l; if ((l = DBFETCH(obj)->properties)) { /* if it has props, then dispose */ delete_proplist(l); DBFETCH(obj)->properties = NULL; } removeobj_ringqueue(obj); DBFETCH(obj)->propsmode = PROPS_UNLOADED; DBFETCH(obj)->propstime = 0; } int disposeprops_notime(dbref obj) { if (DBFETCH(obj)->propsmode == PROPS_UNLOADED) return 0; if (DBFETCH(obj)->propsmode == PROPS_CHANGED) return 0; unloadprops_with_prejudice(obj); return 1; } int disposeprops(dbref obj) { if ((time(NULL) - DBFETCH(obj)->propstime) < tp_clean_interval) return 0; /* don't dispose if less than X minutes old */ return disposeprops_notime(obj); } void dispose_all_oldprops(void) { dbref i; time_t now = time(NULL); for (i = 0; i < db_top; i++) { if ((now - DBFETCH(i)->propstime) >= tp_clean_interval) disposeprops_notime(i); } } void housecleanprops(void) { int limit, max; dbref i, j; if ((proploaded_Q.count < 100) || (proploaded_Q.count < (tp_max_loaded_objs * db_top / 100))) return; limit = 40; max = db_top; i = first_ringqueue_obj(&proploaded_Q); while (limit > 0 && max-- > 0 && i != NOTHING) { j = next_ringqueue_obj(&proploaded_Q, i); if (disposeprops_notime(i)) limit--; i = j; } } int fetchprops_priority(dbref obj, int mode, const char *pdir) { const char *s; int hitflag = 0; /* update fetched timestamp */ DBFETCH(obj)->propstime = time(NULL); /* if in memory, don't try to reload. */ if (DBFETCH(obj)->propsmode != PROPS_UNLOADED) { /* but do update the queue position */ addobject_ringqueue(obj, DBFETCH(obj)->propsmode); if (!pdir) pdir = "/"; while ((s = propdir_unloaded(DBFETCH(obj)->properties, pdir))) { propcache_misses++; hitflag++; if (!mode) update_fetchstats(); if (FLAGS(obj) & SAVED_DELTA) { getproperties(delta_infile, obj, s); } else { getproperties(input_file, obj, s); } } if (hitflag) { return 1; } else { propcache_hits++; return 0; } } propcache_misses++; housecleanprops(); /* actually load in root properties from the appropriate file */ if (FLAGS(obj) & SAVED_DELTA) { getproperties(delta_infile, obj, "/"); } else { getproperties(input_file, obj, "/"); } /* update fetch statistics */ if (!mode) update_fetchstats(); /* add object to appropriate queue */ addobject_ringqueue(obj, ((mode) ? PROPS_PRIORITY : PROPS_LOADED)); return 1; } void fetchprops(dbref obj, const char *pdir) { fetchprops_priority(obj, 0, pdir); } void dirtyprops(dbref obj) { if (DBFETCH(obj)->propsmode == PROPS_CHANGED) return; addobject_ringqueue(obj, PROPS_CHANGED); } void undirtyprops(dbref obj) { if (DBFETCH(obj)->propsmode == PROPS_UNLOADED) return; if (DBFETCH(obj)->propsmode != PROPS_CHANGED) { disposeprops(obj); return; } addobject_ringqueue(obj, PROPS_LOADED); disposeprops(obj); } int propfetch(dbref obj, PropPtr p) { FILE *f; if (!p) return 0; SetPFlags(p, (PropFlags(p) | PROP_TOUCHED)); if (PropFlags(p) & PROP_ISUNLOADED) { f = (FLAGS(obj) & SAVED_DELTA) ? delta_infile : input_file; db_get_single_prop(f, obj, (long) PropDataVal(p), p, NULL); return 1; } return 0; } #endif /* DISKBASE */