1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.store.access.sort.MergeInserter |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.store.access.sort; |
22 | |
23 | import java.util.Vector; |
24 | import org.apache.derby.iapi.services.sanity.SanityManager; |
25 | import org.apache.derby.iapi.services.io.Storable; |
26 | import org.apache.derby.iapi.error.StandardException; |
27 | import org.apache.derby.iapi.store.access.conglomerate.TransactionManager; |
28 | import org.apache.derby.iapi.store.access.ColumnOrdering; |
29 | import org.apache.derby.iapi.store.access.ConglomerateController; |
30 | import org.apache.derby.iapi.store.access.Qualifier; |
31 | import org.apache.derby.iapi.store.access.ScanController; |
32 | import org.apache.derby.iapi.store.access.SortController; |
33 | import org.apache.derby.iapi.store.access.SortInfo; |
34 | import org.apache.derby.iapi.store.access.TransactionController; |
35 | |
36 | import org.apache.derby.iapi.types.DataValueDescriptor; |
37 | |
38 | import org.apache.derby.iapi.types.RowLocation; |
39 | |
40 | /** |
41 | |
42 | |
43 | **/ |
44 | |
45 | public final class MergeInserter implements SortController |
46 | { |
47 | /** |
48 | The sort this inserter is for. |
49 | **/ |
50 | protected MergeSort sort = null; |
51 | |
52 | /** |
53 | The transaction this inserter is in. |
54 | **/ |
55 | protected TransactionManager tran; |
56 | |
57 | /** |
58 | A vector of the conglomerate ids of the merge runs. |
59 | **/ |
60 | Vector mergeRuns = null; |
61 | |
62 | /** |
63 | An in-memory ordered set that is used to sort rows |
64 | before they're sent to merge runs. |
65 | **/ |
66 | SortBuffer sortBuffer = null; |
67 | |
68 | /** |
69 | Information about memory usage to dynamically tune the |
70 | in-memory sort buffer size. |
71 | */ |
72 | long beginFreeMemory; |
73 | long beginTotalMemory; |
74 | long estimatedMemoryUsed; |
75 | boolean avoidMergeRun; // try to avoid merge run if possible |
76 | int runSize; |
77 | int totalRunSize; |
78 | |
79 | protected String stat_sortType; |
80 | protected int stat_numRowsInput; |
81 | protected int stat_numRowsOutput; |
82 | protected int stat_numMergeRuns; |
83 | protected Vector stat_mergeRunsSize; |
84 | |
85 | |
86 | /* |
87 | * Methods of SortController |
88 | */ |
89 | |
90 | /** |
91 | Insert a row into the sort. |
92 | @see SortController#insert |
93 | **/ |
94 | public void insert(DataValueDescriptor[] row) |
95 | throws StandardException |
96 | { |
97 | if (SanityManager.DEBUG) |
98 | { |
99 | // If the sort is null, probably the caller forgot |
100 | // to call initialize. |
101 | SanityManager.ASSERT(sort != null); |
102 | } |
103 | |
104 | // Check that the inserted row is of the correct type |
105 | sort.checkColumnTypes(row); |
106 | |
107 | // Insert the row into the sort buffer, which will |
108 | // sort it into the right order with the rest of the |
109 | // rows and remove any duplicates. |
110 | int insertResult = sortBuffer.insert(row); |
111 | stat_numRowsInput++; |
112 | if (insertResult != SortBuffer.INSERT_DUPLICATE) |
113 | stat_numRowsOutput++; |
114 | if (insertResult == SortBuffer.INSERT_FULL) |
115 | { |
116 | if (avoidMergeRun) |
117 | { |
118 | Runtime jvm = Runtime.getRuntime(); |
119 | if (SanityManager.DEBUG) |
120 | { |
121 | if (SanityManager.DEBUG_ON("SortTuning")) |
122 | { |
123 | jvm.gc(); |
124 | jvm.gc(); |
125 | jvm.gc(); |
126 | } |
127 | } |
128 | |
129 | long currentFreeMemory = jvm.freeMemory(); |
130 | long currentTotalMemory = jvm.totalMemory(); |
131 | |
132 | // before we create an external sort, which is expensive, see if |
133 | // we can use up more in-memory sort buffer |
134 | // we see how much memory has been used between now and the |
135 | // beginning of the sort. Not all of this memory is used by |
136 | // the sort and GC may have kicked in and release some memory. |
137 | // But it is a rough guess. |
138 | estimatedMemoryUsed = (currentTotalMemory-currentFreeMemory) - |
139 | (beginTotalMemory-beginFreeMemory); |
140 | |
141 | if (SanityManager.DEBUG) |
142 | { |
143 | if (SanityManager.DEBUG_ON("SortTuning")) |
144 | { |
145 | SanityManager.DEBUG("SortTuning", |
146 | "Growing sortBuffer dynamically, " + |
147 | "current sortBuffer capacity= " + |
148 | sortBuffer.capacity() + |
149 | " estimatedMemoryUsed = " + estimatedMemoryUsed + |
150 | " currentTotalMemory = " + currentTotalMemory + |
151 | " currentFreeMemory = " + currentFreeMemory + |
152 | " numcolumn = " + row.length + |
153 | " real per row memory = " + |
154 | (estimatedMemoryUsed / sortBuffer.capacity())); |
155 | } |
156 | } |
157 | |
158 | // we want to double the sort buffer size if that will result |
159 | // in the sort to use up no more than 1/2 of all the free |
160 | // memory (including the sort memory) |
161 | // or if GC is so effective we are now using less memory than before |
162 | // or if we are using less than 1Meg of memory and the jvm is |
163 | // using < 5 meg of memory (this indicates that the JVM can |
164 | // afford to be more bloated ?) |
165 | if (estimatedMemoryUsed < 0 || |
166 | ((2*estimatedMemoryUsed) < (estimatedMemoryUsed+currentFreeMemory)/2) || |
167 | (2*estimatedMemoryUsed < ExternalSortFactory.DEFAULT_MEM_USE && |
168 | currentTotalMemory < (5*1024*1024))) |
169 | { |
170 | // ok, double the sort buffer size |
171 | sortBuffer.grow(100); |
172 | |
173 | if (sortBuffer.insert(row) != SortBuffer.INSERT_FULL) |
174 | return; |
175 | } |
176 | |
177 | avoidMergeRun = false; // once we did it, too late to do in |
178 | // memory sort |
179 | } |
180 | |
181 | // The sort buffer became full. Empty it into a |
182 | // merge run, and add the merge run to the vector |
183 | // of merge runs. |
184 | stat_sortType = "external"; |
185 | long conglomid = sort.createMergeRun(tran, sortBuffer); |
186 | if (mergeRuns == null) |
187 | mergeRuns = new Vector(); |
188 | mergeRuns.addElement(new Long(conglomid)); |
189 | |
190 | stat_numMergeRuns++; |
191 | // calculate size of this merge run |
192 | // buffer was too full for last row |
193 | runSize = stat_numRowsInput - totalRunSize - 1; |
194 | totalRunSize += runSize; |
195 | stat_mergeRunsSize.addElement(new Integer(runSize)); |
196 | |
197 | // Re-insert the row into the sort buffer. |
198 | // This is guaranteed to work since the sort |
199 | // buffer has just been emptied. |
200 | sortBuffer.insert(row); |
201 | } |
202 | } |
203 | |
204 | /** |
205 | Close this sort controller. Closing the sort controller |
206 | means the caller is done inserting rows. This method |
207 | must not throw any exceptions since it's called during |
208 | error processing. |
209 | |
210 | @see SortController#close |
211 | **/ |
212 | |
213 | public void close() |
214 | { |
215 | // Tell the sort that we're closed, and hand off |
216 | // the sort buffer and the vector of merge runs. |
217 | if (sort != null) |
218 | sort.doneInserting(this, sortBuffer, mergeRuns); |
219 | |
220 | // if this is an external sort, there will actually |
221 | // be one last merge run with the contents of the |
222 | // current sortBuffer. It will be created when the user |
223 | // reads the result of the sort using openSortScan |
224 | if (stat_sortType == "external") |
225 | { |
226 | stat_numMergeRuns++; |
227 | stat_mergeRunsSize.addElement(new Integer(stat_numRowsInput - totalRunSize)); |
228 | } |
229 | |
230 | // close the SortController in the transaction. |
231 | tran.closeMe(this); |
232 | |
233 | // Clean up. |
234 | sort = null; |
235 | tran = null; |
236 | mergeRuns = null; |
237 | sortBuffer = null; |
238 | } |
239 | |
240 | /* |
241 | * Methods of MergeInserter. Arranged alphabetically. |
242 | */ |
243 | |
244 | /** |
245 | * Return SortInfo object which contains information about the current |
246 | * sort. |
247 | * <p> |
248 | * |
249 | * @see SortInfo |
250 | * |
251 | * @return The SortInfo object which contains info about current sort. |
252 | * |
253 | * @exception StandardException Standard exception policy. |
254 | **/ |
255 | public SortInfo getSortInfo() |
256 | throws StandardException |
257 | { |
258 | return(new MergeSortInfo(this)); |
259 | } |
260 | |
261 | |
262 | /** |
263 | Initialize this inserter. |
264 | @return true if initialization was successful |
265 | **/ |
266 | boolean initialize(MergeSort sort, TransactionManager tran) |
267 | { |
268 | Runtime jvm = Runtime.getRuntime(); |
269 | if (SanityManager.DEBUG) |
270 | { |
271 | if (SanityManager.DEBUG_ON("SortTuning")) |
272 | { |
273 | jvm.gc(); |
274 | jvm.gc(); |
275 | jvm.gc(); |
276 | } |
277 | } |
278 | |
279 | beginFreeMemory = jvm.freeMemory(); |
280 | beginTotalMemory = jvm.totalMemory(); |
281 | estimatedMemoryUsed = 0; |
282 | avoidMergeRun = true; // not an external sort |
283 | stat_sortType = "internal"; |
284 | stat_numMergeRuns = 0; |
285 | stat_numRowsInput = 0; |
286 | stat_numRowsOutput = 0; |
287 | stat_mergeRunsSize = new Vector(); |
288 | runSize = 0; |
289 | totalRunSize = 0; |
290 | |
291 | |
292 | if (SanityManager.DEBUG) |
293 | { |
294 | if (SanityManager.DEBUG_ON("testSort")) |
295 | { |
296 | avoidMergeRun = false; |
297 | } |
298 | } |
299 | |
300 | this.sort = sort; |
301 | this.tran = tran; |
302 | sortBuffer = new SortBuffer(sort); |
303 | if (sortBuffer.init() == false) |
304 | return false; |
305 | return true; |
306 | } |
307 | |
308 | } |