2424import java .io .IOException ;
2525import java .io .InputStream ;
2626import java .util .Base64 ;
27+ import java .util .Collection ;
2728import java .util .Date ;
2829import java .util .HashMap ;
2930import java .util .LinkedList ;
4445import org .apache .zookeeper .data .ACL ;
4546import org .apache .zookeeper .data .StatPersisted ;
4647import org .apache .zookeeper .server .persistence .FileSnap ;
48+ import org .apache .zookeeper .server .persistence .FileTxnSnapLog ;
4749import org .apache .zookeeper .server .persistence .SnapStream ;
4850import org .apache .zookeeper .server .persistence .Util ;
4951import org .apache .zookeeper .util .ServiceUtils ;
5759 */
5860@ InterfaceAudience .Public
5961public class SnapshotFormatter {
62+ public interface Processor <A > {
63+ A apply (DataTree dataTree , Map <Long , Integer > sessions , long lastZxid , A acc ) throws IOException ;
64+ }
65+
66+ public interface Fold <A > {
67+ A apply (DataTree dataTree , String path , A acc ) throws IOException ;
68+ }
69+
6070 private static final Logger LOG = LoggerFactory .getLogger (SnapshotFormatter .class );
6171
6272 private static final String OPT_DUMP_DATA = "d" ;
@@ -65,9 +75,22 @@ public class SnapshotFormatter {
6575
6676 private static final String OPT_DUMP_ACLS = "dump-acls" ;
6777
78+ private static final String OPT_LOAD_DB = "load-db" ;
79+
80+ private static final String OPT_DATA_LOG_DIR = "data-log-dir" ;
81+
6882 // per-znode counter so ncdu treats each as a unique object
6983 private static Integer INODE_IDX = 1000 ;
7084
85+ private final String snapOrSnapDir ;
86+
87+ private final CommandLine commandLine ;
88+
89+ private SnapshotFormatter (String snapOrSnapDir , CommandLine commandLine ) {
90+ this .snapOrSnapDir = snapOrSnapDir ;
91+ this .commandLine = commandLine ;
92+ }
93+
7194 /**
7295 * USAGE: SnapshotFormatter snapshot_file or the ready-made script: zkSnapShotToolkit.sh
7396 */
@@ -95,12 +118,6 @@ public static void main(String[] args) throws Exception {
95118 return ;
96119 }
97120
98- String error = ZKUtil .validateFileInput (snapshotFile );
99- if (null != error ) {
100- LOG .error (error );
101- ServiceUtils .requestSystemExit (ExitCode .INVALID_INVOCATION .getValue ());
102- }
103-
104121 if (cl .hasOption (OPT_DUMP_DATA ) && cl .hasOption (OPT_JSON )) {
105122 LOG .error ("Cannot specify both data dump (-d) and json mode (-json) in same call" );
106123 ServiceUtils .requestSystemExit (ExitCode .INVALID_INVOCATION .getValue ());
@@ -130,6 +147,20 @@ private static Options createOptions() {
130147 .desc ("Dump the ACL entries for each znode" )
131148 .build ());
132149
150+ options .addOption (
151+ Option .builder ()
152+ .longOpt (OPT_LOAD_DB )
153+ .desc ("Also load transaction logs" )
154+ .build ());
155+
156+ options .addOption (
157+ Option .builder ()
158+ .longOpt (OPT_DATA_LOG_DIR )
159+ .desc ("Look for --load-db transaction logs in <dir>" )
160+ .hasArg ()
161+ .argName ("dir" )
162+ .build ());
163+
133164 return options ;
134165 }
135166
@@ -150,17 +181,45 @@ private static void showUsage(Options options, ParseException x) {
150181 ServiceUtils .requestSystemExit (exitCode .getValue ());
151182 }
152183
153- private final String snapshotFileName ;
184+ public void run () throws IOException {
185+ Object acc = this ;
186+ apply (this ::builtinProcessor , acc );
187+ }
154188
155- private final CommandLine commandLine ;
189+ public <A > A apply (Processor <A > processor , A acc ) throws IOException {
190+ if (commandLine .hasOption (OPT_LOAD_DB )) {
191+ return processDataBase (processor , acc );
192+ } else {
193+ return processSnapshot (processor , acc );
194+ }
195+ }
156196
157- private SnapshotFormatter (String snapshotFileName , CommandLine commandLine ) {
158- this .snapshotFileName = snapshotFileName ;
159- this .commandLine = commandLine ;
197+ private <A > A processDataBase (Processor <A > processor , A acc ) throws IOException {
198+ File snapDir = new File (snapOrSnapDir );
199+
200+ File dataLogDir = snapDir ;
201+ if (commandLine .hasOption (OPT_DATA_LOG_DIR )) {
202+ dataLogDir =
203+ new File (commandLine .getOptionValue (OPT_DATA_LOG_DIR ));
204+ }
205+
206+ FileTxnSnapLog snapLog =
207+ new FileTxnSnapLog (dataLogDir , snapDir , /* forWrite */ false );
208+ ZKDatabase zkDb = new ZKDatabase (snapLog );
209+
210+ long lastZxid = zkDb .loadDataBase ();
211+
212+ return processor .apply (zkDb .getDataTree (), zkDb .getSessionWithTimeOuts (), lastZxid , acc );
160213 }
161214
162- public void run () throws IOException {
163- File snapshotFile = new File (snapshotFileName );
215+ private <A > A processSnapshot (Processor <A > processor , A acc ) throws IOException {
216+ String error = ZKUtil .validateFileInput (snapOrSnapDir );
217+ if (null != error ) {
218+ LOG .error (error );
219+ ServiceUtils .requestSystemExit (ExitCode .INVALID_INVOCATION .getValue ());
220+ }
221+
222+ File snapshotFile = new File (snapOrSnapDir );
164223 try (InputStream is = SnapStream .getInputStream (snapshotFile )) {
165224 InputArchive ia = BinaryInputArchive .getArchive (is );
166225
@@ -170,15 +229,29 @@ public void run() throws IOException {
170229 FileSnap .deserialize (dataTree , sessions , ia );
171230 long fileNameZxid = Util .getZxidFromName (snapshotFile .getName (), SNAPSHOT_FILE_PREFIX );
172231
173- if (commandLine .hasOption (OPT_JSON )) {
174- printSnapshotJson (dataTree );
175- } else {
176- printDetails (dataTree , sessions , fileNameZxid );
177- }
232+ return processor .apply (dataTree , sessions , fileNameZxid , acc );
233+ }
234+ }
235+
236+ public Object builtinProcessor (DataTree dataTree , Map <Long , Integer > sessions , long lastZxid , Object acc ) throws IOException {
237+ if (commandLine .hasOption (OPT_JSON )) {
238+ printSnapshotJson (dataTree );
239+ } else {
240+ printDetails (dataTree , sessions , lastZxid );
178241 }
242+ return acc ;
179243 }
180244
181- private void printDetails (DataTree dataTree , Map <Long , Integer > sessions , long fileNameZxid ) {
245+ public static <A > A applyToChildren (DataTree dataTree , String parentPath , Collection <String > childNames , Fold <A > fold , A acc ) throws IOException {
246+ String sep = parentPath .equals ("/" ) ? "" : "/" ;
247+ for (String childName : childNames ) {
248+ String path = parentPath + sep + childName ;
249+ acc = fold .apply (dataTree , path , acc );
250+ }
251+ return acc ;
252+ }
253+
254+ private void printDetails (DataTree dataTree , Map <Long , Integer > sessions , long fileNameZxid ) throws IOException {
182255 long dtZxid = printZnodeDetails (dataTree );
183256 printSessionDetails (dataTree , sessions );
184257 DataTree .ZxidDigest targetZxidDigest = dataTree .getDigestFromLoadedSnapshot ();
@@ -189,19 +262,18 @@ private void printDetails(DataTree dataTree, Map<Long, Integer> sessions, long f
189262 System .out .println (String .format ("----%nLast zxid: 0x%s" , Long .toHexString (Math .max (fileNameZxid , dtZxid ))));
190263 }
191264
192- private long printZnodeDetails (DataTree dataTree ) {
265+ private long printZnodeDetails (DataTree dataTree ) throws IOException {
193266 System .out .println (String .format ("ZNode Details (count=%d):" , dataTree .getNodeCount ()));
194267
195- final long zxid = printZnode (dataTree , "/" );
268+ final long zxid = printZnode (dataTree , "/" , 0L );
196269 System .out .println ("----" );
197270 return zxid ;
198271 }
199272
200- private long printZnode (DataTree dataTree , String name ) {
273+ private Long printZnode (DataTree dataTree , String name , Long zxid ) throws IOException {
201274 System .out .println ("----" );
202275 DataNode n = dataTree .getNode (name );
203276 Set <String > children ;
204- long zxid ;
205277 synchronized (n ) { // keep findbugs happy
206278 System .out .println (name );
207279 printStat (n .stat );
@@ -232,10 +304,7 @@ private long printZnode(DataTree dataTree, String name) {
232304 }
233305 }
234306 if (children != null ) {
235- for (String child : children ) {
236- long cxid = printZnode (dataTree , name + (name .equals ("/" ) ? "" : "/" ) + child );
237- zxid = Math .max (zxid , cxid );
238- }
307+ zxid = applyToChildren (dataTree , name , children , this ::printZnode , zxid );
239308 }
240309 return zxid ;
241310 }
@@ -259,15 +328,15 @@ private static String formatAclEntry(ACL aclEntry) {
259328 return b .toString ();
260329 }
261330
262- private void printSessionDetails (DataTree dataTree , Map <Long , Integer > sessions ) {
331+ private static void printSessionDetails (DataTree dataTree , Map <Long , Integer > sessions ) {
263332 System .out .println ("Session Details (sid, timeout, ephemeralCount):" );
264333 for (Map .Entry <Long , Integer > e : sessions .entrySet ()) {
265334 long sid = e .getKey ();
266335 System .out .println (String .format ("%#016x, %d, %d" , sid , e .getValue (), dataTree .getEphemerals (sid ).size ()));
267336 }
268337 }
269338
270- private void printStat (StatPersisted stat ) {
339+ private static void printStat (StatPersisted stat ) {
271340 printHex ("cZxid" , stat .getCzxid ());
272341 System .out .println (" ctime = " + new Date (stat .getCtime ()).toString ());
273342 printHex ("mZxid" , stat .getMzxid ());
@@ -279,11 +348,11 @@ private void printStat(StatPersisted stat) {
279348 printHex ("ephemeralOwner" , stat .getEphemeralOwner ());
280349 }
281350
282- private void printHex (String prefix , long value ) {
351+ private static void printHex (String prefix , long value ) {
283352 System .out .println (String .format (" %s = %#016x" , prefix , value ));
284353 }
285354
286- private void printSnapshotJson (final DataTree dataTree ) {
355+ private static void printSnapshotJson (final DataTree dataTree ) throws IOException {
287356 JsonStringEncoder encoder = JsonStringEncoder .getInstance ();
288357 System .out .printf (
289358 "[1,0,{\" progname\" :\" SnapshotFormatter.java\" ,\" progver\" :\" 0.01\" ,\" timestamp\" :%d}" ,
@@ -292,14 +361,14 @@ private void printSnapshotJson(final DataTree dataTree) {
292361 System .out .print ("]" );
293362 }
294363
295- private void printZnodeJson (final DataTree dataTree , final String fullPath , JsonStringEncoder encoder ) {
364+ private static JsonStringEncoder printZnodeJson (final DataTree dataTree , final String fullPath , JsonStringEncoder encoder ) throws IOException {
296365
297366
298367 final DataNode n = dataTree .getNode (fullPath );
299368
300369 if (null == n ) {
301370 LOG .warn ("DataTree Node for {} doesn't exist" , fullPath );
302- return ;
371+ return encoder ;
303372 }
304373
305374 final String name = fullPath .equals ("/" )
@@ -327,13 +396,11 @@ private void printZnodeJson(final DataTree dataTree, final String fullPath, Json
327396 }
328397 if (children != null && children .size () > 0 ) {
329398 System .out .print ("[" + nodeSB );
330- for (String child : children ) {
331- printZnodeJson (dataTree , fullPath + (fullPath .equals ("/" ) ? "" : "/" ) + child , encoder );
332- }
399+ encoder = applyToChildren (dataTree , fullPath , children , SnapshotFormatter ::printZnodeJson , encoder );
333400 System .out .print ("]" );
334401 } else {
335402 System .out .print (nodeSB );
336403 }
404+ return encoder ;
337405 }
338-
339406}
0 commit comments