Commit 7f5dda11 authored by Jim Fulton's avatar Jim Fulton

We recently added estimated_size. We originally added it as a new

unsigned long field after a signed char state field and a 3-character
reserved field.  This didn't work because there are packages in the
wild that have their own copies of cPersistence.h that didn't see the
update.

To get around this, we used the reserved space by making
estimated_size a 24-bit bit field in the space occupied by the old
3-character reserved field.  To fit in 24 bits, we made the units of
estimated_size 64-character blocks.  This allows is to handle up to a
GB.  We should never see that, but to be paranoid, we also truncate
sizes greater than 1GB.  We also set the minimum size to 64 bytes.
parent ddfa05a4
...@@ -323,7 +323,22 @@ PreviousBucket(Bucket **current, Bucket *first) ...@@ -323,7 +323,22 @@ PreviousBucket(Bucket **current, Bucket *first)
trailing = first; trailing = first;
PER_USE_OR_RETURN(first, -1); PER_USE_OR_RETURN(first, -1);
first = first->next; first = first->next;
PER_UNUSE(trailing);
((trailing)->state==cPersistent_STICKY_STATE
&&
((trailing)->state=cPersistent_UPTODATE_STATE));
PER_ACCESSED(trailing);
if (first == *current) { if (first == *current) {
*current = trailing; *current = trailing;
......
...@@ -89,7 +89,8 @@ unghostify(cPersistentObject *self) ...@@ -89,7 +89,8 @@ unghostify(cPersistentObject *self)
if (self->cache) { if (self->cache) {
/* Create a node in the ring for this unghostified object. */ /* Create a node in the ring for this unghostified object. */
self->cache->non_ghost_count++; self->cache->non_ghost_count++;
self->cache->total_estimated_size += self->estimated_size; self->cache->total_estimated_size +=
_estimated_size_in_bytes(self->estimated_size);
ring_add(&self->cache->ring_home, &self->ring); ring_add(&self->cache->ring_home, &self->ring);
Py_INCREF(self); Py_INCREF(self);
} }
...@@ -145,7 +146,8 @@ unlink_from_ring(cPersistentObject *self) ...@@ -145,7 +146,8 @@ unlink_from_ring(cPersistentObject *self)
/* if we're ghostifying an object, we better have some non-ghosts */ /* if we're ghostifying an object, we better have some non-ghosts */
assert(self->cache->non_ghost_count > 0); assert(self->cache->non_ghost_count > 0);
self->cache->non_ghost_count--; self->cache->non_ghost_count--;
self->cache->total_estimated_size -= self->estimated_size; self->cache->total_estimated_size -=
_estimated_size_in_bytes(self->estimated_size);
ring_del(&self->ring); ring_del(&self->ring);
} }
...@@ -176,7 +178,8 @@ ghostify(cPersistentObject *self) ...@@ -176,7 +178,8 @@ ghostify(cPersistentObject *self)
/* If we're ghostifying an object, we better have some non-ghosts. */ /* If we're ghostifying an object, we better have some non-ghosts. */
assert(self->cache->non_ghost_count > 0); assert(self->cache->non_ghost_count > 0);
self->cache->non_ghost_count--; self->cache->non_ghost_count--;
self->cache->total_estimated_size -= self->estimated_size; self->cache->total_estimated_size -=
_estimated_size_in_bytes(self->estimated_size);
ring_del(&self->ring); ring_del(&self->ring);
self->state = cPersistent_GHOST_STATE; self->state = cPersistent_GHOST_STATE;
dictptr = _PyObject_GetDictPtr((PyObject *)self); dictptr = _PyObject_GetDictPtr((PyObject *)self);
...@@ -1017,29 +1020,30 @@ Per_get_state(cPersistentObject *self) ...@@ -1017,29 +1020,30 @@ Per_get_state(cPersistentObject *self)
static PyObject * static PyObject *
Per_get_estimated_size(cPersistentObject *self) Per_get_estimated_size(cPersistentObject *self)
{ {
return PyInt_FromLong(self->estimated_size); return PyInt_FromLong(_estimated_size_in_bytes(self->estimated_size));
} }
static int static int
Per_set_estimated_size(cPersistentObject *self, PyObject *v) Per_set_estimated_size(cPersistentObject *self, PyObject *v)
{ {
if (v) { if (v) {
if (PyInt_Check(v)) { if (PyInt_Check(v)) {
if (PyInt_AS_LONG(v) < 0) { long lv = PyInt_AS_LONG(v);
PyErr_SetString(PyExc_ValueError, if (lv < 0) {
"_p_estimated_size must not be negative"); PyErr_SetString(PyExc_ValueError,
return -1; "_p_estimated_size must not be negative");
} return -1;
self->estimated_size = PyInt_AS_LONG(v); }
} self->estimated_size = _estimated_size_in_24_bits(lv);
else { }
PyErr_SetString(PyExc_ValueError, else {
"_p_estimated_size must be an integer"); PyErr_SetString(PyExc_ValueError,
return -1; "_p_estimated_size must be an integer");
} return -1;
} else }
self->estimated_size = 0; } else
return 0; self->estimated_size = 0;
return 0;
} }
static PyGetSetDef Per_getsets[] = { static PyGetSetDef Per_getsets[] = {
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
PyObject_HEAD \ PyObject_HEAD \
CPersistentRing ring_home; \ CPersistentRing ring_home; \
int non_ghost_count; \ int non_ghost_count; \
PY_LONG_LONG total_estimated_size; /* total estimated size of items in cache */ PY_LONG_LONG total_estimated_size;
struct ccobject_head_struct; struct ccobject_head_struct;
...@@ -60,9 +60,28 @@ typedef struct ccobject_head_struct PerCache; ...@@ -60,9 +60,28 @@ typedef struct ccobject_head_struct PerCache;
PerCache *cache; \ PerCache *cache; \
CPersistentRing ring; \ CPersistentRing ring; \
char serial[8]; \ char serial[8]; \
signed char state; \ signed state:8; \
unsigned char reserved[3]; \ unsigned estimated_size:24;
unsigned long estimated_size;
/* We recently added estimated_size. We originally added it as a new
unsigned long field after a signed char state field and a
3-character reserved field. This didn't work because there
are packages in the wild that have their own copies of cPersistence.h
that didn't see the update.
To get around this, we used the reserved space by making
estimated_size a 24-bit bit field in the space occupied by the old
3-character reserved field. To fit in 24 bits, we made the units
of estimated_size 64-character blocks. This allows is to handle up
to a GB. We should never see that, but to be paranoid, we also
truncate sizes greater than 1GB. We also set the minimum size to
64 bytes.
We use the _estimated_size_in_24_bits and _estimated_size_in_bytes
macros both to avoid repetition and to make intent a little clearer.
*/
#define _estimated_size_in_24_bits(I) ((I) > 1073741696 ? 16777215 : (I)/64+1)
#define _estimated_size_in_bytes(I) ((I)*64)
#define cPersistent_GHOST_STATE -1 #define cPersistent_GHOST_STATE -1
#define cPersistent_UPTODATE_STATE 0 #define cPersistent_UPTODATE_STATE 0
......
...@@ -638,27 +638,29 @@ cc_ringlen(ccobject *self) ...@@ -638,27 +638,29 @@ cc_ringlen(ccobject *self)
static PyObject * static PyObject *
cc_update_object_size_estimation(ccobject *self, PyObject *args) cc_update_object_size_estimation(ccobject *self, PyObject *args)
{ {
PyObject *oid; PyObject *oid;
cPersistentObject *v; cPersistentObject *v;
unsigned int new_size; unsigned int new_size;
if (!PyArg_ParseTuple(args, "OI:updateObjectSizeEstimation", &oid, &new_size)) if (!PyArg_ParseTuple(args, "OI:updateObjectSizeEstimation", &oid, &new_size))
return NULL; return NULL;
/* Note: reference borrowed */ /* Note: reference borrowed */
v = (cPersistentObject *)PyDict_GetItem(self->data, oid); v = (cPersistentObject *)PyDict_GetItem(self->data, oid);
if (v) { if (v) {
/* we know this object -- update our "total_size_estimation" /* we know this object -- update our "total_size_estimation"
we must only update when the object is in the ring we must only update when the object is in the ring
*/ */
if (v->ring.r_next) { if (v->ring.r_next) {
self->total_estimated_size += new_size - v->estimated_size; self->total_estimated_size += _estimated_size_in_bytes(
/* we do this in "Connection" as we need it even when the _estimated_size_in_24_bits(new_size) - v->estimated_size
object is not in the cache (or not the ring) );
*/ /* we do this in "Connection" as we need it even when the
/* v->estimated_size = new_size; */ object is not in the cache (or not the ring)
} */
/* v->estimated_size = new_size; */
} }
Py_RETURN_NONE; }
} Py_RETURN_NONE;
}
static struct PyMethodDef cc_methods[] = { static struct PyMethodDef cc_methods[] = {
...@@ -1058,7 +1060,8 @@ static PyGetSetDef cc_getsets[] = { ...@@ -1058,7 +1060,8 @@ static PyGetSetDef cc_getsets[] = {
static PyMemberDef cc_members[] = { static PyMemberDef cc_members[] = {
{"cache_size", T_INT, offsetof(ccobject, cache_size)}, {"cache_size", T_INT, offsetof(ccobject, cache_size)},
{"cache_size_bytes", T_LONG, offsetof(ccobject, cache_size_bytes)}, {"cache_size_bytes", T_LONG, offsetof(ccobject, cache_size_bytes)},
{"total_estimated_size", T_LONG, offsetof(ccobject, total_estimated_size), RO}, {"total_estimated_size", T_LONG, offsetof(ccobject, total_estimated_size),
RO},
{"cache_drain_resistance", T_INT, {"cache_drain_resistance", T_INT,
offsetof(ccobject, cache_drain_resistance)}, offsetof(ccobject, cache_drain_resistance)},
{"cache_non_ghost_count", T_INT, offsetof(ccobject, non_ghost_count), RO}, {"cache_non_ghost_count", T_INT, offsetof(ccobject, non_ghost_count), RO},
......
...@@ -94,7 +94,13 @@ Of course, the estimated size must not be negative. ...@@ -94,7 +94,13 @@ Of course, the estimated size must not be negative.
0 0
>>> p._p_estimated_size = 1000 >>> p._p_estimated_size = 1000
>>> p._p_estimated_size >>> p._p_estimated_size
1000 1024
Huh? Why is the estimated size coming out different than what we put
in? The reason is that the size isn't stored exactly. For backward
compatibility reasons, the size needs to fit in 24 bits, so,
internally, it is adjusted somewhat.
>>> p._p_estimated_size = -1 >>> p._p_estimated_size = -1
Traceback (most recent call last): Traceback (most recent call last):
.... ....
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment