Julian Reschke wrote:
At the end of the day, what we should do is *measure* the performance of
JCR2SPI compared to native implementations. I'll try to submit a few
tests soon.
...
OK, I've got tests (not polished) and numbers.
Scenario:
A collection /a/b with 500 members, each 1024 in size, content type
application/octet-stream.
Test code that obtains all members, checking content type, size, and
total number.
My store can do that in ~80ms.
Why doing it through SPI (with limited batch read support), it will take
~1500ms.
Wrapping that with JCR2SPI, it takes around ~2700ms.
So it seems we need drastically remove the overhead introduced by the
SPI API.
Test code below:
private String createTestCollJcr(String p_parentpath, int p_members,
int p_size) throws Exception {
Repository l_repository = getRepository();
Session l_session = null;
try {
l_session = l_repository.login(getCredentials());
Node l_folder = null;
try {
l_folder = (Node)l_session.getItem(p_parentpath + "/bigcoll");
}
catch (RepositoryException ex) {
// nothing to do
}
// delete when needed
if (l_folder != null) {
l_folder.remove();
l_session.save();
}
Node l_parent = (Node)l_session.getItem(p_parentpath);
l_folder = l_parent.addNode("bigcoll", "nt:folder");
assertNotNull(l_folder);
long l_cnt = 0;
while (l_cnt < p_members) {
InputStream l_is = new BufferedInputStream(new
ContentGenerator(p_size), p_size);
Node l_new = l_folder.addNode("tst" + l_cnt, "nt:file");
Node l_cnew = l_new.addNode("jcr:content", "nt:resource");
l_cnew.setProperty("jcr:data", l_is);
l_cnew.setProperty("jcr:mimeType", "application/octet-stream");
l_session.save();
l_cnt += 1;
}
}
finally {
if (l_session != null) {
l_session.logout();
}
}
return p_parentpath + "/bigcoll";
}
private static int BIGCOLLMEMBERS = 500;
private static int BIGCOLLMEMBERSIZE = 1024;
private static String BIGCOLLMIMETYPE = "application/octet-stream";
public void testGetMembersSpi() throws Exception {
String l_path = createTestColl(this.m_path, BIGCOLLMEMBERS,
BIGCOLLMEMBERSIZE);
RepositoryService l_rs = getRepositoryService();
SessionInfo l_si = null;
try {
l_si = l_rs.obtain(getCredentials(), null);
long l_start = System.currentTimeMillis();
long l_cnt = 0;
while (System.currentTimeMillis() - l_start < MS || l_cnt < 5) {
NodeId l_nid = TestPerf.computeNodeId(l_rs, l_si, l_path);
int l_members = 0;
for (Iterator<ChildInfo> l_it =
(Iterator<ChildInfo>)l_rs.getChildInfos(l_si, l_nid); l_it.hasNext(); ) {
ChildInfo l_c = l_it.next();
assertNotNull(l_c);
NodeId l_cnid =
l_rs.getIdFactory().createNodeId(l_c.getUniqueID());
NodeInfo l_node = null;
NodeInfo l_contentnode = null;
PropertyInfo l_mimetype = null;
PropertyInfo l_data = null;
Iterator l_iteminfos = l_rs.getItemInfos(l_si, l_cnid);
l_node = (NodeInfo)l_iteminfos.next();
assertNotNull(l_node);
while (l_iteminfos.hasNext()) {
ItemInfo l_i = (ItemInfo)l_iteminfos.next();
if (l_i.getParentId().equals(l_node.getId()) &&
NameConstants.JCR_CONTENT.equals(l_i.getName())) {
l_contentnode = (NodeInfo)l_i;
}
if (l_contentnode != null &&
l_i.getParentId().equals(l_contentnode.getId()) &&
NameConstants.JCR_MIMETYPE.equals(l_i.getName())) {
l_mimetype = (PropertyInfo)l_i;
}
if (l_contentnode != null &&
l_i.getParentId().equals(l_contentnode.getId()) &&
NameConstants.JCR_DATA.equals(l_i.getName())) {
l_data = (PropertyInfo)l_i;
}
}
if (l_contentnode == null) {
// explicitly fetch the content node, it wasn't returned
with the parent
NodeId l_contentnodeid =
l_rs.getIdFactory().createNodeId(l_c.getUniqueID(),
l_rs.getPathFactory().create(NameConstants.JCR_CONTENT));
Iterator l_iteminfos2 = l_rs.getItemInfos(l_si,
l_contentnodeid);
l_contentnode = (NodeInfo)l_iteminfos2.next();
while (l_iteminfos2.hasNext()) {
ItemInfo l_i = (ItemInfo)l_iteminfos2.next();
if (l_i.getParentId().equals(l_contentnode.getId()) &&
NameConstants.JCR_MIMETYPE.equals(l_i.getName())) {
l_mimetype = (PropertyInfo)l_i;
}
if (l_i.getParentId().equals(l_contentnode.getId()) &&
NameConstants.JCR_DATA.equals(l_i.getName())) {
l_data = (PropertyInfo)l_i;
}
}
}
assertNotNull(l_contentnode);
if (l_mimetype == null) {
// explicitly fetch the mime type property, it wasn't
returned with the parent
PropertyId l_mimetypeid =
l_rs.getIdFactory().createPropertyId(l_contentnode.getId(),
NameConstants.JCR_MIMETYPE);
l_mimetype = l_rs.getPropertyInfo(l_si, l_mimetypeid);
}
assertNotNull(l_mimetype);
assertEquals(BIGCOLLMIMETYPE,
l_mimetype.getValues()[0].getString());
if (l_data == null) {
// explicitly fetch the mime type property, it wasn't
returned with the parent
PropertyId l_dataid =
l_rs.getIdFactory().createPropertyId(l_contentnode.getId(),
NameConstants.JCR_DATA);
l_data = l_rs.getPropertyInfo(l_si, l_dataid);
}
assertNotNull(l_data);
assertEquals(BIGCOLLMEMBERSIZE,
l_data.getValues()[0].getLength());
l_members += 1;
}
assertEquals(BIGCOLLMEMBERS, l_members);
l_cnt += 1;
}
long l_elapsed = System.currentTimeMillis() - l_start;
LOG.info(String.format("GetMembers - SPI: %.4fms per call (%d
iterations)", (double)l_elapsed / l_cnt, l_cnt));
}
finally {
if (l_si != null) {
l_rs.dispose(l_si);
}
}
}
public void testGetMembersJcr() throws Exception {
String l_path = createTestCollJcr(this.m_path, BIGCOLLMEMBERS,
BIGCOLLMEMBERSIZE);
Repository l_repository = getRepository();
Session l_session = null;
try {
l_session = l_repository.login(getCredentials(), null);
long l_start = System.currentTimeMillis();
long l_cnt = 0;
while (System.currentTimeMillis() - l_start < MS || l_cnt < 5) {
Node l_dir = (Node)l_session.getItem(l_path);
assertNotNull(l_dir);
int l_members = 0;
for (NodeIterator l_it = l_dir.getNodes(); l_it.hasNext(); ) {
Node l_c = l_it.nextNode();
Node l_e = l_c.getNode("jcr:content");
String l_type = l_e.getProperty("jcr:mimeType").getString();
long l_length = l_e.getProperty("jcr:data").getLength();
assertTrue(l_c.isNode());
assertEquals(BIGCOLLMIMETYPE, l_type);
assertEquals(BIGCOLLMEMBERSIZE, l_length);
l_members += 1;
}
assertEquals(BIGCOLLMEMBERS, l_members);
l_session.refresh(false);
l_cnt += 1;
}
long l_elapsed = System.currentTimeMillis() - l_start;
LOG.info(String.format("GetMembers - JCR: %.4fms per call (%d
iterations)", (double)l_elapsed / l_cnt, l_cnt));
}
finally {
if (l_session != null) {
l_session.logout();
}
}
}
private class ContentGenerator extends InputStream {
private long m_length;
private long m_position;
public ContentGenerator(long p_length) {
this.m_length = p_length;
this.m_position = 0;
}
public int read() {
if (this.m_position++ < this.m_length) {
return 0;
}
else {
return -1;
}
}
}