1 /* 2 * Copyright (C) 2011, 2013 Google Inc., and others. 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 44 package org.eclipse.jgit.internal.storage.dfs; 45 46 import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK; 47 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE; 48 49 import java.util.Arrays; 50 51 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; 52 import org.eclipse.jgit.internal.storage.pack.PackExt; 53 import org.eclipse.jgit.internal.storage.reftable.ReftableWriter; 54 import org.eclipse.jgit.storage.pack.PackStatistics; 55 56 /** 57 * Description of a DFS stored pack/index file. 58 * <p> 59 * Implementors may extend this class and add additional data members. 60 * <p> 61 * Instances of this class are cached with the DfsPackFile, and should not be 62 * modified once initialized and presented to the JGit DFS library. 63 */ 64 public class DfsPackDescription implements Comparable<DfsPackDescription> { 65 private final DfsRepositoryDescription repoDesc; 66 private final String packName; 67 private PackSource packSource; 68 private long lastModified; 69 private long[] sizeMap; 70 private int[] blockSizeMap; 71 private long objectCount; 72 private long deltaCount; 73 private long minUpdateIndex; 74 private long maxUpdateIndex; 75 76 private PackStatistics packStats; 77 private ReftableWriter.Stats refStats; 78 private int extensions; 79 private int indexVersion; 80 private long estimatedPackSize; 81 82 /** 83 * Initialize a description by pack name and repository. 84 * <p> 85 * The corresponding index file is assumed to exist. If this is not true 86 * implementors must extend the class and override 87 * {@link #getFileName(PackExt)}. 88 * <p> 89 * Callers should also try to fill in other fields if they are reasonably 90 * free to access at the time this instance is being initialized. 91 * 92 * @param name 93 * name of the pack file. Must end with ".pack". 94 * @param repoDesc 95 * description of the repo containing the pack file. 96 */ 97 public DfsPackDescription(DfsRepositoryDescription repoDesc, String name) { 98 this.repoDesc = repoDesc; 99 int dot = name.lastIndexOf('.'); 100 this.packName = (dot < 0) ? name : name.substring(0, dot); 101 102 int extCnt = PackExt.values().length; 103 sizeMap = new long[extCnt]; 104 blockSizeMap = new int[extCnt]; 105 } 106 107 /** 108 * Get description of the repository. 109 * 110 * @return description of the repository. 111 */ 112 public DfsRepositoryDescription getRepositoryDescription() { 113 return repoDesc; 114 } 115 116 /** 117 * Adds the pack file extension to the known list. 118 * 119 * @param ext 120 * the file extension 121 */ 122 public void addFileExt(PackExt ext) { 123 extensions |= ext.getBit(); 124 } 125 126 /** 127 * Whether the pack file extension is known to exist. 128 * 129 * @param ext 130 * the file extension 131 * @return whether the pack file extension is known to exist. 132 */ 133 public boolean hasFileExt(PackExt ext) { 134 return (extensions & ext.getBit()) != 0; 135 } 136 137 /** 138 * Get file name 139 * 140 * @param ext 141 * the file extension 142 * @return name of the file. 143 */ 144 public String getFileName(PackExt ext) { 145 return packName + '.' + ext.getExtension(); 146 } 147 148 /** 149 * Get cache key for use by the block cache. 150 * 151 * @param ext 152 * the file extension. 153 * @return cache key for use by the block cache. 154 */ 155 public DfsStreamKey getStreamKey(PackExt ext) { 156 return DfsStreamKey.of(getRepositoryDescription(), getFileName(ext), 157 ext); 158 } 159 160 /** 161 * Get the source of the pack. 162 * 163 * @return the source of the pack. 164 */ 165 public PackSource getPackSource() { 166 return packSource; 167 } 168 169 /** 170 * Set the source of the pack. 171 * 172 * @param source 173 * the source of the pack. 174 * @return {@code this} 175 */ 176 public DfsPackDescription setPackSource(PackSource source) { 177 packSource = source; 178 return this; 179 } 180 181 /** 182 * Get time the pack was created, in milliseconds. 183 * 184 * @return time the pack was created, in milliseconds. 185 */ 186 public long getLastModified() { 187 return lastModified; 188 } 189 190 /** 191 * Set time the pack was created, in milliseconds. 192 * 193 * @param timeMillis 194 * time the pack was created, in milliseconds. 0 if not known. 195 * @return {@code this} 196 */ 197 public DfsPackDescription setLastModified(long timeMillis) { 198 lastModified = timeMillis; 199 return this; 200 } 201 202 /** 203 * Get minUpdateIndex for the reftable, if present. 204 * 205 * @return minUpdateIndex for the reftable, if present. 206 */ 207 public long getMinUpdateIndex() { 208 return minUpdateIndex; 209 } 210 211 /** 212 * Set minUpdateIndex for the reftable. 213 * 214 * @param min 215 * minUpdateIndex for the reftable. 216 * @return {@code this} 217 */ 218 public DfsPackDescription setMinUpdateIndex(long min) { 219 minUpdateIndex = min; 220 return this; 221 } 222 223 /** 224 * Get maxUpdateIndex for the reftable, if present. 225 * 226 * @return maxUpdateIndex for the reftable, if present. 227 */ 228 public long getMaxUpdateIndex() { 229 return maxUpdateIndex; 230 } 231 232 /** 233 * Set maxUpdateIndex for the reftable. 234 * 235 * @param max 236 * maxUpdateIndex for the reftable. 237 * @return {@code this} 238 */ 239 public DfsPackDescription setMaxUpdateIndex(long max) { 240 maxUpdateIndex = max; 241 return this; 242 } 243 244 /** 245 * Set size of the file in bytes. 246 * 247 * @param ext 248 * the file extension. 249 * @param bytes 250 * size of the file in bytes. If 0 the file is not known and will 251 * be determined on first read. 252 * @return {@code this} 253 */ 254 public DfsPackDescription setFileSize(PackExt ext, long bytes) { 255 int i = ext.getPosition(); 256 if (i >= sizeMap.length) { 257 sizeMap = Arrays.copyOf(sizeMap, i + 1); 258 } 259 sizeMap[i] = Math.max(0, bytes); 260 return this; 261 } 262 263 /** 264 * Get size of the file, in bytes. 265 * 266 * @param ext 267 * the file extension. 268 * @return size of the file, in bytes. If 0 the file size is not yet known. 269 */ 270 public long getFileSize(PackExt ext) { 271 int i = ext.getPosition(); 272 return i < sizeMap.length ? sizeMap[i] : 0; 273 } 274 275 /** 276 * Get blockSize of the file, in bytes. 277 * 278 * @param ext 279 * the file extension. 280 * @return blockSize of the file, in bytes. If 0 the blockSize size is not 281 * yet known and may be discovered when opening the file. 282 */ 283 public int getBlockSize(PackExt ext) { 284 int i = ext.getPosition(); 285 return i < blockSizeMap.length ? blockSizeMap[i] : 0; 286 } 287 288 /** 289 * Set blockSize of the file, in bytes. 290 * 291 * @param ext 292 * the file extension. 293 * @param blockSize 294 * blockSize of the file, in bytes. If 0 the blockSize is not 295 * known and will be determined on first read. 296 * @return {@code this} 297 */ 298 public DfsPackDescription setBlockSize(PackExt ext, int blockSize) { 299 int i = ext.getPosition(); 300 if (i >= blockSizeMap.length) { 301 blockSizeMap = Arrays.copyOf(blockSizeMap, i + 1); 302 } 303 blockSizeMap[i] = Math.max(0, blockSize); 304 return this; 305 } 306 307 /** 308 * Set estimated size of the .pack file in bytes. 309 * 310 * @param estimatedPackSize 311 * estimated size of the .pack file in bytes. If 0 the pack file 312 * size is unknown. 313 * @return {@code this} 314 */ 315 public DfsPackDescription setEstimatedPackSize(long estimatedPackSize) { 316 this.estimatedPackSize = Math.max(0, estimatedPackSize); 317 return this; 318 } 319 320 /** 321 * Get estimated size of the .pack file in bytes. 322 * 323 * @return estimated size of the .pack file in bytes. If 0 the pack file 324 * size is unknown. 325 */ 326 public long getEstimatedPackSize() { 327 return estimatedPackSize; 328 } 329 330 /** 331 * Get number of objects in the pack. 332 * 333 * @return number of objects in the pack. 334 */ 335 public long getObjectCount() { 336 return objectCount; 337 } 338 339 /** 340 * Set number of objects in the pack. 341 * 342 * @param cnt 343 * number of objects in the pack. 344 * @return {@code this} 345 */ 346 public DfsPackDescription setObjectCount(long cnt) { 347 objectCount = Math.max(0, cnt); 348 return this; 349 } 350 351 /** 352 * Get number of delta compressed objects in the pack. 353 * 354 * @return number of delta compressed objects in the pack. 355 */ 356 public long getDeltaCount() { 357 return deltaCount; 358 } 359 360 /** 361 * Set number of delta compressed objects in the pack. 362 * 363 * @param cnt 364 * number of delta compressed objects in the pack. 365 * @return {@code this} 366 */ 367 public DfsPackDescription setDeltaCount(long cnt) { 368 deltaCount = Math.max(0, cnt); 369 return this; 370 } 371 372 /** 373 * Get statistics from PackWriter, if the pack was built with it. 374 * 375 * @return statistics from PackWriter, if the pack was built with it. 376 * Generally this is only available for packs created by 377 * DfsGarbageCollector or DfsPackCompactor, and only when the pack 378 * is being committed to the repository. 379 */ 380 public PackStatistics getPackStats() { 381 return packStats; 382 } 383 384 DfsPackDescription setPackStats(PackStatistics stats) { 385 this.packStats = stats; 386 setFileSize(PACK, stats.getTotalBytes()); 387 setObjectCount(stats.getTotalObjects()); 388 setDeltaCount(stats.getTotalDeltas()); 389 return this; 390 } 391 392 /** 393 * Get stats from the sibling reftable, if created. 394 * 395 * @return stats from the sibling reftable, if created. 396 */ 397 public ReftableWriter.Stats getReftableStats() { 398 return refStats; 399 } 400 401 void setReftableStats(ReftableWriter.Stats stats) { 402 this.refStats = stats; 403 setMinUpdateIndex(stats.minUpdateIndex()); 404 setMaxUpdateIndex(stats.maxUpdateIndex()); 405 setFileSize(REFTABLE, stats.totalBytes()); 406 setBlockSize(REFTABLE, stats.refBlockSize()); 407 } 408 409 /** 410 * Discard the pack statistics, if it was populated. 411 * 412 * @return {@code this} 413 */ 414 public DfsPackDescription clearPackStats() { 415 packStats = null; 416 refStats = null; 417 return this; 418 } 419 420 /** 421 * Get the version of the index file written. 422 * 423 * @return the version of the index file written. 424 */ 425 public int getIndexVersion() { 426 return indexVersion; 427 } 428 429 /** 430 * Set the version of the index file written. 431 * 432 * @param version 433 * the version of the index file written. 434 * @return {@code this} 435 */ 436 public DfsPackDescription setIndexVersion(int version) { 437 indexVersion = version; 438 return this; 439 } 440 441 /** {@inheritDoc} */ 442 @Override 443 public int hashCode() { 444 return packName.hashCode(); 445 } 446 447 /** {@inheritDoc} */ 448 @Override 449 public boolean equals(Object b) { 450 if (b instanceof DfsPackDescription) { 451 DfsPackDescription desc = (DfsPackDescription) b; 452 return packName.equals(desc.packName) && 453 getRepositoryDescription().equals(desc.getRepositoryDescription()); 454 } 455 return false; 456 } 457 458 /** 459 * {@inheritDoc} 460 * <p> 461 * Sort packs according to the optimal lookup ordering. 462 * <p> 463 * This method tries to position packs in the order readers should examine 464 * them when looking for objects by SHA-1. The default tries to sort packs 465 * with more recent modification dates before older packs, and packs with 466 * fewer objects before packs with more objects. 467 */ 468 @Override 469 public int compareTo(DfsPackDescription b) { 470 // Cluster by PackSource, pushing UNREACHABLE_GARBAGE to the end. 471 PackSource as = getPackSource(); 472 PackSource bs = b.getPackSource(); 473 if (as != null && bs != null) { 474 int cmp = as.category - bs.category; 475 if (cmp != 0) 476 return cmp; 477 } 478 479 // Tie break GC type packs by smallest first. There should be at most 480 // one of each source, but when multiple exist concurrent GCs may have 481 // run. Preferring the smaller file selects higher quality delta 482 // compression, placing less demand on the DfsBlockCache. 483 if (as != null && as == bs && isGC(as)) { 484 int cmp = Long.signum(getFileSize(PACK) - b.getFileSize(PACK)); 485 if (cmp != 0) { 486 return cmp; 487 } 488 } 489 490 // Newer packs should sort first. 491 int cmp = Long.signum(b.getLastModified() - getLastModified()); 492 if (cmp != 0) 493 return cmp; 494 495 // Break ties on smaller index. Readers may get lucky and find 496 // the object they care about in the smaller index. This also pushes 497 // big historical packs to the end of the list, due to more objects. 498 return Long.signum(getObjectCount() - b.getObjectCount()); 499 } 500 501 static boolean isGC(PackSource s) { 502 switch (s) { 503 case GC: 504 case GC_REST: 505 case GC_TXN: 506 return true; 507 default: 508 return false; 509 } 510 } 511 512 /** {@inheritDoc} */ 513 @Override 514 public String toString() { 515 return getFileName(PackExt.PACK); 516 } 517 }