Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

DhtReader.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /*
  2. * Copyright (C) 2011, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.storage.dht;
  44. import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
  45. import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
  46. import java.io.IOException;
  47. import java.io.PrintWriter;
  48. import java.io.StringWriter;
  49. import java.lang.reflect.Field;
  50. import java.lang.reflect.Modifier;
  51. import java.util.ArrayList;
  52. import java.util.Collection;
  53. import java.util.Collections;
  54. import java.util.LinkedHashMap;
  55. import java.util.List;
  56. import java.util.Map;
  57. import java.util.concurrent.TimeoutException;
  58. import java.util.zip.Inflater;
  59. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  60. import org.eclipse.jgit.errors.MissingObjectException;
  61. import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
  62. import org.eclipse.jgit.generated.storage.dht.proto.GitStore.CachedPackInfo;
  63. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  64. import org.eclipse.jgit.lib.AnyObjectId;
  65. import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
  66. import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
  67. import org.eclipse.jgit.lib.InflaterCache;
  68. import org.eclipse.jgit.lib.ObjectId;
  69. import org.eclipse.jgit.lib.ObjectLoader;
  70. import org.eclipse.jgit.lib.ObjectReader;
  71. import org.eclipse.jgit.lib.ProgressMonitor;
  72. import org.eclipse.jgit.revwalk.ObjectWalk;
  73. import org.eclipse.jgit.revwalk.RevCommit;
  74. import org.eclipse.jgit.revwalk.RevObject;
  75. import org.eclipse.jgit.revwalk.RevWalk;
  76. import org.eclipse.jgit.storage.dht.spi.Context;
  77. import org.eclipse.jgit.storage.dht.spi.Database;
  78. import org.eclipse.jgit.storage.dht.spi.ObjectIndexTable;
  79. import org.eclipse.jgit.storage.pack.CachedPack;
  80. import org.eclipse.jgit.storage.pack.ObjectReuseAsIs;
  81. import org.eclipse.jgit.storage.pack.ObjectToPack;
  82. import org.eclipse.jgit.storage.pack.PackOutputStream;
  83. import org.eclipse.jgit.storage.pack.PackWriter;
  84. /**
  85. * ObjectReader implementation for DHT based repositories.
  86. * <p>
  87. * This class is public only to expose its unique statistics for runtime
  88. * performance reporting. Applications should always prefer to use the more
  89. * generic base class, {@link ObjectReader}.
  90. */
  91. public class DhtReader extends ObjectReader implements ObjectReuseAsIs {
  92. private final DhtRepository repository;
  93. private final RepositoryKey repo;
  94. private final Database db;
  95. private final DhtReaderOptions readerOptions;
  96. private final DhtInserterOptions inserterOptions;
  97. private final Statistics stats;
  98. private final RecentInfoCache recentInfo;
  99. private final RecentChunks recentChunks;
  100. private final DeltaBaseCache deltaBaseCache;
  101. private Collection<CachedPack> cachedPacks;
  102. private Inflater inflater;
  103. private Prefetcher prefetcher;
  104. DhtReader(DhtObjDatabase objdb) {
  105. this.repository = objdb.getRepository();
  106. this.repo = objdb.getRepository().getRepositoryKey();
  107. this.db = objdb.getDatabase();
  108. this.readerOptions = objdb.getReaderOptions();
  109. this.inserterOptions = objdb.getInserterOptions();
  110. this.stats = new Statistics();
  111. this.recentInfo = new RecentInfoCache(getOptions());
  112. this.recentChunks = new RecentChunks(this);
  113. this.deltaBaseCache = new DeltaBaseCache(this);
  114. }
  115. /** @return describes how this DhtReader has performed. */
  116. public Statistics getStatistics() {
  117. return stats;
  118. }
  119. Database getDatabase() {
  120. return db;
  121. }
  122. RepositoryKey getRepositoryKey() {
  123. return repo;
  124. }
  125. DhtReaderOptions getOptions() {
  126. return readerOptions;
  127. }
  128. DhtInserterOptions getInserterOptions() {
  129. return inserterOptions;
  130. }
  131. RecentInfoCache getRecentInfoCache() {
  132. return recentInfo;
  133. }
  134. RecentChunks getRecentChunks() {
  135. return recentChunks;
  136. }
  137. DeltaBaseCache getDeltaBaseCache() {
  138. return deltaBaseCache;
  139. }
  140. Inflater inflater() {
  141. if (inflater == null)
  142. inflater = InflaterCache.get();
  143. else
  144. inflater.reset();
  145. return inflater;
  146. }
  147. @Override
  148. public void release() {
  149. recentChunks.clear();
  150. endPrefetch();
  151. InflaterCache.release(inflater);
  152. inflater = null;
  153. super.release();
  154. }
  155. @Override
  156. public ObjectReader newReader() {
  157. return new DhtReader(repository.getObjectDatabase());
  158. }
  159. @Override
  160. public boolean has(AnyObjectId objId, int typeHint) throws IOException {
  161. if (objId instanceof RefDataUtil.IdWithChunk)
  162. return true;
  163. if (recentChunks.has(repo, objId))
  164. return true;
  165. if (repository.getRefDatabase().findChunk(objId) != null)
  166. return true;
  167. return !find(objId).isEmpty();
  168. }
  169. @Override
  170. public ObjectLoader open(AnyObjectId objId, int typeHint)
  171. throws MissingObjectException, IncorrectObjectTypeException,
  172. IOException {
  173. ObjectLoader ldr = recentChunks.open(repo, objId, typeHint);
  174. if (ldr != null)
  175. return ldr;
  176. ChunkAndOffset p = getChunk(objId, typeHint, false);
  177. ldr = PackChunk.read(p.chunk, p.offset, this, typeHint);
  178. recentChunk(p.chunk);
  179. return ldr;
  180. }
  181. @Override
  182. public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
  183. Iterable<T> objectIds, boolean reportMissing) {
  184. return new OpenQueue<T>(this, objectIds, reportMissing);
  185. }
  186. @Override
  187. public long getObjectSize(AnyObjectId objectId, int typeHint)
  188. throws MissingObjectException, IncorrectObjectTypeException,
  189. IOException {
  190. for (ObjectInfo info : find(objectId))
  191. return info.getSize();
  192. throw missing(objectId, typeHint);
  193. }
  194. @Override
  195. public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
  196. Iterable<T> objectIds, boolean reportMissing) {
  197. return new SizeQueue<T>(this, objectIds, reportMissing);
  198. }
  199. @Override
  200. public void walkAdviceBeginCommits(RevWalk rw, Collection<RevCommit> roots)
  201. throws IOException {
  202. endPrefetch();
  203. // Don't assign the prefetcher right away. Delay until its
  204. // configured as push might invoke our own methods that may
  205. // try to call back into the active prefetcher.
  206. //
  207. Prefetcher p = prefetch(OBJ_COMMIT, readerOptions.getWalkCommitsPrefetchRatio());
  208. p.push(this, roots);
  209. prefetcher = p;
  210. }
  211. @Override
  212. public void walkAdviceBeginTrees(ObjectWalk ow, RevCommit min, RevCommit max)
  213. throws IOException {
  214. endPrefetch();
  215. // Don't assign the prefetcher right away. Delay until its
  216. // configured as push might invoke our own methods that may
  217. // try to call back into the active prefetcher.
  218. //
  219. Prefetcher p = prefetch(OBJ_TREE, readerOptions.getWalkTreesPrefetchRatio());
  220. p.push(this, min.getTree(), max.getTree());
  221. prefetcher = p;
  222. }
  223. @Override
  224. public void walkAdviceEnd() {
  225. endPrefetch();
  226. }
  227. void recentChunk(PackChunk chunk) {
  228. recentChunks.put(chunk);
  229. }
  230. ChunkAndOffset getChunkGently(AnyObjectId objId) {
  231. return recentChunks.find(repo, objId);
  232. }
  233. ChunkAndOffset getChunk(AnyObjectId objId, int typeHint, boolean checkRecent)
  234. throws DhtException, MissingObjectException {
  235. if (checkRecent) {
  236. ChunkAndOffset r = recentChunks.find(repo, objId);
  237. if (r != null)
  238. return r;
  239. }
  240. ChunkKey key;
  241. if (objId instanceof RefDataUtil.IdWithChunk)
  242. key = ((RefDataUtil.IdWithChunk) objId).getChunkKey();
  243. else
  244. key = repository.getRefDatabase().findChunk(objId);
  245. if (key != null) {
  246. PackChunk chunk = load(key);
  247. if (chunk != null && chunk.hasIndex()) {
  248. int pos = chunk.findOffset(repo, objId);
  249. if (0 <= pos)
  250. return new ChunkAndOffset(chunk, pos);
  251. }
  252. // The hint above is stale. Fall through and do a
  253. // more exhaustive lookup to find the object.
  254. }
  255. if (prefetcher != null) {
  256. ChunkAndOffset r = prefetcher.find(repo, objId);
  257. if (r != null)
  258. return r;
  259. }
  260. for (ObjectInfo link : find(objId)) {
  261. PackChunk chunk;
  262. if (prefetcher != null) {
  263. chunk = prefetcher.get(link.getChunkKey());
  264. if (chunk == null) {
  265. chunk = load(link.getChunkKey());
  266. if (chunk == null)
  267. continue;
  268. if (prefetcher.isType(typeHint))
  269. prefetcher.push(chunk.getMeta());
  270. }
  271. } else {
  272. chunk = load(link.getChunkKey());
  273. if (chunk == null)
  274. continue;
  275. }
  276. return new ChunkAndOffset(chunk, link.getOffset());
  277. }
  278. throw missing(objId, typeHint);
  279. }
  280. ChunkKey findChunk(AnyObjectId objId) throws DhtException {
  281. if (objId instanceof RefDataUtil.IdWithChunk)
  282. return ((RefDataUtil.IdWithChunk) objId).getChunkKey();
  283. ChunkKey key = repository.getRefDatabase().findChunk(objId);
  284. if (key != null)
  285. return key;
  286. ChunkAndOffset r = recentChunks.find(repo, objId);
  287. if (r != null)
  288. return r.chunk.getChunkKey();
  289. for (ObjectInfo link : find(objId))
  290. return link.getChunkKey();
  291. return null;
  292. }
  293. static MissingObjectException missing(AnyObjectId objId, int typeHint) {
  294. ObjectId id = objId.copy();
  295. if (typeHint != OBJ_ANY)
  296. return new MissingObjectException(id, typeHint);
  297. return new MissingObjectException(id, DhtText.get().objectTypeUnknown);
  298. }
  299. PackChunk getChunk(ChunkKey key) throws DhtException {
  300. PackChunk chunk = recentChunks.get(key);
  301. if (chunk != null)
  302. return chunk;
  303. chunk = load(key);
  304. if (chunk != null)
  305. return chunk;
  306. throw new DhtMissingChunkException(key);
  307. }
  308. @Override
  309. public Collection<ObjectId> resolve(AbbreviatedObjectId id)
  310. throws IOException {
  311. // Because ObjectIndexKey requires at least 4 leading digits
  312. // don't resolve anything that is shorter than 4 digits.
  313. //
  314. if (id.length() < 4)
  315. return Collections.emptySet();
  316. throw new DhtException.TODO("resolve abbreviations");
  317. }
  318. public DhtObjectToPack newObjectToPack(RevObject obj) {
  319. return new DhtObjectToPack(obj);
  320. }
  321. @SuppressWarnings("unchecked")
  322. public void selectObjectRepresentation(PackWriter packer,
  323. ProgressMonitor monitor, Iterable<ObjectToPack> objects)
  324. throws IOException, MissingObjectException {
  325. Iterable itr = objects;
  326. new RepresentationSelector(packer, this, monitor).select(itr);
  327. }
  328. private Prefetcher prefetch(final int type, final int ratio) {
  329. int limit = readerOptions.getChunkLimit();
  330. int prefetchLimit = (int) (limit * (ratio / 100.0));
  331. recentChunks.setMaxBytes(limit - prefetchLimit);
  332. return new Prefetcher(this, type, prefetchLimit);
  333. }
  334. private void endPrefetch() {
  335. recentChunks.setMaxBytes(getOptions().getChunkLimit());
  336. prefetcher = null;
  337. }
  338. @SuppressWarnings("unchecked")
  339. public void writeObjects(PackOutputStream out, List<ObjectToPack> objects)
  340. throws IOException {
  341. prefetcher = prefetch(0, readerOptions.getWriteObjectsPrefetchRatio());
  342. try {
  343. List itr = objects;
  344. new ObjectWriter(this, prefetcher).plan(itr);
  345. for (ObjectToPack otp : objects)
  346. out.writeObject(otp);
  347. } finally {
  348. endPrefetch();
  349. }
  350. }
  351. public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
  352. boolean validate) throws IOException,
  353. StoredObjectRepresentationNotAvailableException {
  354. DhtObjectToPack obj = (DhtObjectToPack) otp;
  355. try {
  356. PackChunk chunk = recentChunks.get(obj.chunk);
  357. if (chunk == null) {
  358. chunk = prefetcher.get(obj.chunk);
  359. if (chunk == null) {
  360. // This should never happen during packing, it implies
  361. // the fetch plan was incorrect. Unfortunately that can
  362. // occur if objects need to be recompressed on the fly.
  363. //
  364. stats.access(obj.chunk).cntCopyObjectAsIs_PrefetchMiss++;
  365. chunk = getChunk(obj.chunk);
  366. }
  367. if (!chunk.isFragment())
  368. recentChunk(chunk);
  369. }
  370. chunk.copyObjectAsIs(out, obj, validate, this);
  371. } catch (DhtMissingChunkException missingChunk) {
  372. stats.access(missingChunk.getChunkKey()).cntCopyObjectAsIs_InvalidChunk++;
  373. throw new StoredObjectRepresentationNotAvailableException(otp);
  374. }
  375. }
  376. public Collection<CachedPack> getCachedPacks() throws IOException {
  377. if (cachedPacks == null) {
  378. Collection<CachedPackInfo> info;
  379. Collection<CachedPack> packs;
  380. try {
  381. info = db.repository().getCachedPacks(repo);
  382. } catch (TimeoutException e) {
  383. throw new DhtTimeoutException(e);
  384. }
  385. packs = new ArrayList<CachedPack>(info.size());
  386. for (CachedPackInfo i : info)
  387. packs.add(new DhtCachedPack(i));
  388. cachedPacks = packs;
  389. }
  390. return cachedPacks;
  391. }
  392. public void copyPackAsIs(PackOutputStream out, CachedPack pack,
  393. boolean validate) throws IOException {
  394. ((DhtCachedPack) pack).copyAsIs(out, validate, this);
  395. }
  396. private List<ObjectInfo> find(AnyObjectId obj) throws DhtException {
  397. List<ObjectInfo> info = recentInfo.get(obj);
  398. if (info != null)
  399. return info;
  400. stats.cntObjectIndex_Load++;
  401. ObjectIndexKey idxKey = ObjectIndexKey.create(repo, obj);
  402. Context opt = Context.READ_REPAIR;
  403. Sync<Map<ObjectIndexKey, Collection<ObjectInfo>>> sync = Sync.create();
  404. db.objectIndex().get(opt, Collections.singleton(idxKey), sync);
  405. try {
  406. Collection<ObjectInfo> m;
  407. m = sync.get(getOptions().getTimeout()).get(idxKey);
  408. if (m == null || m.isEmpty())
  409. return Collections.emptyList();
  410. info = new ArrayList<ObjectInfo>(m);
  411. ObjectInfo.sort(info);
  412. recentInfo.put(obj, info);
  413. return info;
  414. } catch (InterruptedException e) {
  415. throw new DhtTimeoutException(e);
  416. } catch (TimeoutException e) {
  417. throw new DhtTimeoutException(e);
  418. }
  419. }
  420. private PackChunk load(ChunkKey chunkKey) throws DhtException {
  421. if (0 == stats.access(chunkKey).cntReader_Load++
  422. && readerOptions.isTrackFirstChunkLoad())
  423. stats.access(chunkKey).locReader_Load = new Throwable("first");
  424. Context opt = Context.READ_REPAIR;
  425. Sync<Collection<PackChunk.Members>> sync = Sync.create();
  426. db.chunk().get(opt, Collections.singleton(chunkKey), sync);
  427. try {
  428. Collection<PackChunk.Members> c = sync.get(getOptions()
  429. .getTimeout());
  430. if (c.isEmpty())
  431. return null;
  432. if (c instanceof List)
  433. return ((List<PackChunk.Members>) c).get(0).build();
  434. return c.iterator().next().build();
  435. } catch (InterruptedException e) {
  436. throw new DhtTimeoutException(e);
  437. } catch (TimeoutException e) {
  438. throw new DhtTimeoutException(e);
  439. }
  440. }
  441. static class ChunkAndOffset {
  442. final PackChunk chunk;
  443. final int offset;
  444. ChunkAndOffset(PackChunk chunk, int offset) {
  445. this.chunk = chunk;
  446. this.offset = offset;
  447. }
  448. }
  449. /** How this DhtReader has performed since creation. */
  450. public static class Statistics {
  451. private final Map<ChunkKey, ChunkAccess> chunkAccess = new LinkedHashMap<ChunkKey, ChunkAccess>();
  452. ChunkAccess access(ChunkKey chunkKey) {
  453. ChunkAccess ca = chunkAccess.get(chunkKey);
  454. if (ca == null) {
  455. ca = new ChunkAccess(chunkKey);
  456. chunkAccess.put(chunkKey, ca);
  457. }
  458. return ca;
  459. }
  460. /**
  461. * Number of sequential {@link ObjectIndexTable} lookups made by the
  462. * reader. These were made without the support of batch lookups.
  463. */
  464. public int cntObjectIndex_Load;
  465. /** Cycles detected in delta chains during OBJ_REF_DELTA reads. */
  466. public int deltaChainCycles;
  467. int recentChunks_Hits;
  468. int recentChunks_Miss;
  469. int deltaBaseCache_Hits;
  470. int deltaBaseCache_Miss;
  471. /** @return ratio of recent chunk hits, [0.00,1.00]. */
  472. public double getRecentChunksHitRatio() {
  473. int total = recentChunks_Hits + recentChunks_Miss;
  474. return ((double) recentChunks_Hits) / total;
  475. }
  476. /** @return ratio of delta base cache hits, [0.00,1.00]. */
  477. public double getDeltaBaseCacheHitRatio() {
  478. int total = deltaBaseCache_Hits + deltaBaseCache_Miss;
  479. return ((double) deltaBaseCache_Hits) / total;
  480. }
  481. /**
  482. * @return collection of chunk accesses made by the application code
  483. * against this reader. The collection's iterator has no
  484. * relevant order.
  485. */
  486. public Collection<ChunkAccess> getChunkAccess() {
  487. return chunkAccess.values();
  488. }
  489. @Override
  490. public String toString() {
  491. StringBuilder b = new StringBuilder();
  492. b.append("DhtReader.Statistics:\n");
  493. b.append(" ");
  494. if (recentChunks_Hits != 0 || recentChunks_Miss != 0)
  495. ratio(b, "recentChunks", getRecentChunksHitRatio());
  496. if (deltaBaseCache_Hits != 0 || deltaBaseCache_Miss != 0)
  497. ratio(b, "deltaBaseCache", getDeltaBaseCacheHitRatio());
  498. appendFields(this, b);
  499. b.append("\n");
  500. for (ChunkAccess ca : getChunkAccess()) {
  501. b.append(" ");
  502. b.append(ca.toString());
  503. b.append("\n");
  504. }
  505. return b.toString();
  506. }
  507. @SuppressWarnings("boxing")
  508. static void ratio(StringBuilder b, String name, double value) {
  509. b.append(String.format(" %s=%.2f%%", name, value * 100.0));
  510. }
  511. static void appendFields(Object obj, StringBuilder b) {
  512. try {
  513. for (Field field : obj.getClass().getDeclaredFields()) {
  514. String n = field.getName();
  515. if (field.getType() == Integer.TYPE
  516. && (field.getModifiers() & Modifier.PUBLIC) != 0) {
  517. int v = field.getInt(obj);
  518. if (0 < v)
  519. b.append(' ').append(n).append('=').append(v);
  520. }
  521. }
  522. } catch (IllegalArgumentException e) {
  523. throw new RuntimeException(e);
  524. } catch (IllegalAccessException e) {
  525. throw new RuntimeException(e);
  526. }
  527. }
  528. /** Summary describing how a chunk was accessed. */
  529. public static final class ChunkAccess {
  530. /** Chunk this access block describes. */
  531. public final ChunkKey chunkKey;
  532. /**
  533. * Number of times chunk was loaded sequentially. Incremented when
  534. * the reader had to load the chunk on demand with no cache or
  535. * prefetcher support.
  536. */
  537. public int cntReader_Load;
  538. Throwable locReader_Load;
  539. /**
  540. * Number of times the prefetcher loaded from the database.
  541. * Incremented each time the prefetcher asked for the chunk from the
  542. * underlying database (which might have its own distributed cache,
  543. * or not).
  544. */
  545. public int cntPrefetcher_Load;
  546. /**
  547. * Number of times the prefetcher ordering was wrong. Incremented if
  548. * a reader wants a chunk but the prefetcher didn't have it ready at
  549. * the time of request. This indicates a bad prefetching plan as the
  550. * chunk should have been listed earlier in the prefetcher's list.
  551. */
  552. public int cntPrefetcher_OutOfOrder;
  553. /**
  554. * Number of times the reader had to stall to wait for a chunk that
  555. * is currently being prefetched to finish loading and become ready.
  556. * This indicates the prefetcher may have fetched other chunks first
  557. * (had the wrong order), or does not have a deep enough window to
  558. * hide these loads from the application.
  559. */
  560. public int cntPrefetcher_WaitedForLoad;
  561. /**
  562. * Number of times the reader asked the prefetcher for the same
  563. * chunk after it was already consumed from the prefetcher. This
  564. * indicates the reader has walked back on itself and revisited a
  565. * chunk again.
  566. */
  567. public int cntPrefetcher_Revisited;
  568. /**
  569. * Number of times the reader needed this chunk to copy an object
  570. * as-is into a pack stream, but the prefetcher didn't have it
  571. * ready. This correlates with {@link #cntPrefetcher_OutOfOrder} or
  572. * {@link #cntPrefetcher_Revisited}.
  573. */
  574. public int cntCopyObjectAsIs_PrefetchMiss;
  575. /**
  576. * Number of times the reader tried to copy an object from this
  577. * chunk, but discovered the chunk was corrupt or did not contain
  578. * the object as expected.
  579. */
  580. public int cntCopyObjectAsIs_InvalidChunk;
  581. ChunkAccess(ChunkKey key) {
  582. chunkKey = key;
  583. }
  584. @Override
  585. public String toString() {
  586. StringBuilder b = new StringBuilder();
  587. b.append(chunkKey).append('[');
  588. appendFields(this, b);
  589. b.append(" ]");
  590. if (locReader_Load != null) {
  591. StringWriter sw = new StringWriter();
  592. locReader_Load.printStackTrace(new PrintWriter(sw));
  593. b.append(sw);
  594. }
  595. return b.toString();
  596. }
  597. }
  598. }
  599. }