For reproducibility.
In [24]:
import IPython
IPython.__version__
Out[24]:
In [25]:
import ultrastorage
ultrastorage.__version__
Out[25]:
Import related modules.
In [26]:
from ultrastorage.storageunit import StorageUnit
from ultrastorage.item import Item
Create a storage unit named myStorageUnit with capacity 100 and 2 CPUs.
In [27]:
storage_unit = StorageUnit(capacity=100, cpu=2, name="myStorageUnit")
print(storage_unit)
Display various data of this storage unit.
In [28]:
# total capacity
print("capacity={}".format(storage_unit.capacity))
# number of items
print("count={}".format(storage_unit.count()))
# number of cpy (i.e., number of parallel operations)
print("cpu={}".format(storage_unit.cpu))
# free space (i.e., capacity minus total size minus set aside)
print("free_space={}".format(storage_unit.free_space()))
# load (i.e., total size divided by capacity)
print("load={}".format(storage_unit.load()))
# name
print("name={}".format(storage_unit.name))
# reserved memory
print("reserved_memory={}".format(storage_unit.reserved_memory))
# total size (i.e., sum of the size of the items)
print("size={}".format(storage_unit.size()))
In [29]:
storage_unit.is_empty()
Out[29]:
The capacity of the storage unit is a mandatory field. The default number of CPUs is 1 and the storage unit is given a name in case it is not given.
In [30]:
print(StorageUnit(100))
Note that, once created, it is not possible to modify the capacity and the number of CPUs of the storage unit. Also, it is not possible to rename a storage unit.
In [31]:
storage_unit = StorageUnit(capacity=100, cpu=2, name="myStorageUnit")
print(storage_unit)
Add 9 items with sizes 1, 2, 3, 4, 5, 6, 7, 8 and 9 to the storage unit.
In [32]:
for i in range(1,10):
storage_unit.add_item(Item(i))
print("{} {}".format(i, storage_unit))
storage_unit.is_empty()
Out[32]:
In [33]:
print("len={}".format(len(storage_unit)))
print("is_empty={}".format(storage_unit.is_empty()))
Visinting a storage unit reduces to visiting the stored items. Notice that a storage unit does visit the items in some arbitrary order.
In [34]:
",".join(str(item) for item in storage_unit)
Out[34]:
Some specific iterators.
In [35]:
# items by descending size
",".join(str(item) for item in storage_unit.items_by_descending_size())
Out[35]:
In [36]:
# items by ascending size
",".join(str(item) for item in storage_unit.items_by_ascending_size())
Out[36]:
One can get randomly (uniform) choosen item.
In [37]:
item = storage_unit.random_item()
print("item={}".format(item))
In [38]:
storage_unit = StorageUnit(capacity=100, cpu=2, name="myStorageUnit")
print(storage_unit)
One can only delete items using item references and not item sizes. After all it would be silly to try to delete a file giving its size only.
In [39]:
items = [Item(i) for i in range(1,5)]
storage_unit.add_items(items)
",".join(str(item) for item in storage_unit)
Out[39]:
In [40]:
storage_unit.delete_item(items[2])
",".join(str(item) for item in storage_unit)
Out[40]:
Deleting an item that is not stored in the storage unit raises an exception.
In [41]:
storage_unit.delete_item(Item(5))
In [42]:
storage_unit = StorageUnit(capacity=100, cpu=2, name="myStorageUnit")
print(storage_unit)
A storage unit is not a multi set, you cannot add the same item twice.
In [43]:
item = Item(1)
storage_unit.add_item(item)
storage_unit.add_item(item)
But, of course, distinct items with the same size can be added.
In [44]:
storage_unit.add_item(Item(2))
storage_unit.add_item(Item(2))
print(",".join(str(item) for item in storage_unit))
For convenience, you can add several items with a single function.
In [45]:
items = [Item(i) for i in range(2,6)]
storage_unit.add_items(items)
print(storage_unit)
print(",".join(str(item) for item in storage_unit))
Items are managed using a reference count like strategy. The storage unit maintains a reference count for each item, allowing the user to retain the item (i.e., increment the associated reference count by 1) and to release the item (i.e., decremenent the associated reference count by 1). Deleting an item reduces to releasing an item. An item cannot be effectively deleted unless its reference count drops to 0.
In [46]:
storage_unit = StorageUnit(capacity=100, cpu=2, name="myStorageUnit")
item = Item(1)
storage_unit.add_item(item)
storage_unit.retain_item(item)
storage_unit.delete_item(item)
print(",".join(str(item) for item in storage_unit))
In [47]:
storage_unit.delete_item(item) # or storage_unit.release_item(item)
storage_unit.is_empty()
Out[47]:
Setting aside to freeze some available space that is not considered as free space. This is particularly useful in case a monitoring system is copying a large item to a storage unit.
In [48]:
storage_unit = StorageUnit(capacity=100, cpu=2, name="myStorageUnit")
print("storage uit={}".format(storage_unit))
print("reserved memory={}".format(storage_unit.reserved_memory))
In [49]:
storage_unit.reserve_memory(50)
print("storage unit={}".format(storage_unit))
print("reserved memory={}".format(storage_unit.reserved_memory))
Whereas there is no item in this storage unit with capacity $100$, the free space takes into account the reserved memory/
In [50]:
storage_unit.free_space()
Out[50]:
In [51]:
storage_unit.capacity
Out[51]:
In [52]:
storage_unit.reserved_memory
Out[52]:
In the current situration, it is thus not possible to add any item whose size is larger than 50.
In [53]:
storage_unit.add_item(Item(51))
One can restore (i.e., return back) the reserved memory. Note that the only constraint is to restore at most the reserved memory.
In [54]:
storage_unit.restore_memory(1)
print("storage unit={}".format(storage_unit))
storage_unit.add_item(Item(51))
print("storage unit={}".format(storage_unit))
storage_unit.restore_memory(storage_unit.reserved_memory)
print("storage unit={}".format(storage_unit))