1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.dfs;
12
13 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
14 import org.eclipse.jgit.internal.storage.io.BlockSource;
15 import org.eclipse.jgit.internal.storage.pack.PackExt;
16 import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
17 import org.eclipse.jgit.internal.storage.reftable.ReftableCompactor;
18 import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
19 import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
20 import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
21 import org.eclipse.jgit.lib.Ref;
22 import org.eclipse.jgit.transport.ReceiveCommand;
23
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 import java.util.Set;
31
32 import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
33
34
35
36
37
38 public class DfsReftableBatchRefUpdate extends ReftableBatchRefUpdate {
39 private static final int AVG_BYTES = 36;
40
41 private final DfsReftableDatabase refdb;
42
43 private final DfsObjDatabase odb;
44
45
46
47
48
49
50
51
52
53 protected DfsReftableBatchRefUpdate(DfsReftableDatabase refdb,
54 DfsObjDatabase odb) {
55 super(refdb, refdb.reftableDatabase, refdb.getLock(), refdb.getRepository());
56 this.refdb = refdb;
57 this.odb = odb;
58 }
59
60 @Override
61 protected void applyUpdates(List<Ref> newRefs, List<ReceiveCommand> pending)
62 throws IOException {
63 Set<DfsPackDescription> prune = Collections.emptySet();
64 DfsPackDescription pack = odb.newPack(PackSource.INSERT);
65 try (DfsOutputStream out = odb.writeFile(pack, REFTABLE)) {
66 ReftableConfig cfg = DfsPackCompactor
67 .configureReftable(refdb.getReftableConfig(), out);
68
69 ReftableWriter.Stats stats;
70 if (refdb.compactDuringCommit()
71 && newRefs.size() * AVG_BYTES <= cfg.getRefBlockSize()
72 && canCompactTopOfStack(cfg)) {
73 ByteArrayOutputStream tmp = new ByteArrayOutputStream();
74 ReftableWriter rw = new ReftableWriter(cfg, tmp);
75 write(rw, newRefs, pending);
76 rw.finish();
77 stats = compactTopOfStack(out, cfg, tmp.toByteArray());
78 prune = toPruneTopOfStack();
79 } else {
80 ReftableWriter rw = new ReftableWriter(cfg, out);
81 write(rw, newRefs, pending);
82 rw.finish();
83 stats = rw.getStats();
84 }
85 pack.addFileExt(REFTABLE);
86 pack.setReftableStats(stats);
87 }
88
89 odb.commitPack(Collections.singleton(pack), prune);
90 odb.addReftable(pack, prune);
91 refdb.clearCache();
92 }
93
94 private boolean canCompactTopOfStack(ReftableConfig cfg)
95 throws IOException {
96 refdb.getLock().lock();
97 try {
98 DfsReftableStack stack = refdb.stack();
99 List<ReftableReader> readers = stack.readers();
100 if (readers.isEmpty()) {
101 return false;
102 }
103
104 int lastIdx = readers.size() - 1;
105 DfsReftable last = stack.files().get(lastIdx);
106 DfsPackDescription desc = last.getPackDescription();
107 if (desc.getPackSource() != PackSource.INSERT
108 || !packOnlyContainsReftable(desc)) {
109 return false;
110 }
111
112 ReftableReader table = readers.get(lastIdx);
113 int bs = cfg.getRefBlockSize();
114 return table.size() <= 3 * bs;
115 } finally {
116 refdb.getLock().unlock();
117 }
118 }
119
120 private ReftableWriter.Stats compactTopOfStack(OutputStream out,
121 ReftableConfig cfg, byte[] newTable) throws IOException {
122 refdb.getLock().lock();
123 try {
124 List<ReftableReader> stack = refdb.stack().readers();
125
126 ReftableReader last = stack.get(stack.size() - 1);
127
128 List<ReftableReader> tables = new ArrayList<>(2);
129 tables.add(last);
130 tables.add(new ReftableReader(BlockSource.from(newTable)));
131
132 ReftableCompactor compactor = new ReftableCompactor(out);
133 compactor.setConfig(cfg);
134 compactor.setIncludeDeletes(true);
135 compactor.addAll(tables);
136 compactor.compact();
137 return compactor.getStats();
138 } finally {
139 refdb.getLock().unlock();
140 }
141 }
142
143 private Set<DfsPackDescription> toPruneTopOfStack() throws IOException {
144 refdb.getLock().lock();
145 try {
146 List<DfsReftable> stack = refdb.stack().files();
147
148 DfsReftable last = stack.get(stack.size() - 1);
149 return Collections.singleton(last.getPackDescription());
150 } finally {
151 refdb.getLock().unlock();
152 }
153 }
154
155 private boolean packOnlyContainsReftable(DfsPackDescription desc) {
156 for (PackExt ext : PackExt.values()) {
157 if (ext != REFTABLE && desc.hasFileExt(ext)) {
158 return false;
159 }
160 }
161 return true;
162 }
163 }