1+ package org .tron .plugins ;
2+
3+ import static java .nio .charset .StandardCharsets .UTF_8 ;
4+ import static org .iq80 .leveldb .impl .Iq80DBFactory .factory ;
5+
6+ import com .beust .jcommander .JCommander ;
7+ import com .beust .jcommander .Parameter ;
8+ import java .io .BufferedInputStream ;
9+ import java .io .File ;
10+ import java .io .FileInputStream ;
11+ import java .io .IOException ;
12+ import java .io .InputStream ;
13+ import java .nio .charset .StandardCharsets ;
14+ import java .nio .file .Path ;
15+ import java .nio .file .Paths ;
16+ import java .util .ArrayList ;
17+ import java .util .Arrays ;
18+ import java .util .List ;
19+ import java .util .Objects ;
20+ import java .util .Properties ;
21+ import java .util .concurrent .ArrayBlockingQueue ;
22+ import java .util .concurrent .Callable ;
23+ import java .util .concurrent .ExecutionException ;
24+ import java .util .concurrent .Executors ;
25+ import java .util .concurrent .Future ;
26+ import java .util .concurrent .ThreadPoolExecutor ;
27+ import java .util .concurrent .TimeUnit ;
28+ import java .util .stream .Collectors ;
29+ import lombok .extern .slf4j .Slf4j ;
30+ import org .iq80 .leveldb .CompressionType ;
31+ import org .iq80 .leveldb .DB ;
32+ import org .iq80 .leveldb .Options ;
33+ import org .iq80 .leveldb .impl .Filename ;
34+
35+ @ Slf4j (topic = "archive" )
36+ /*
37+ a helper to rewrite leveldb manifest.
38+ */
39+ public class ArchiveManifest implements Callable <Boolean > {
40+
41+
42+ private static final String KEY_ENGINE = "ENGINE" ;
43+ private static final String LEVELDB = "LEVELDB" ;
44+
45+ private final Path srcDbPath ;
46+ private final String name ;
47+ private final Options options ;
48+ private final long startTime ;
49+
50+
51+ private static final int CPUS = Runtime .getRuntime ().availableProcessors ();
52+
53+ private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor (
54+ CPUS , 16 * CPUS , 1 , TimeUnit .MINUTES ,
55+ new ArrayBlockingQueue <>(CPUS , true ), Executors .defaultThreadFactory (),
56+ new ThreadPoolExecutor .CallerRunsPolicy ());
57+
58+ static {
59+ EXECUTOR .allowCoreThreadTimeOut (true );
60+ }
61+
62+ public ArchiveManifest (String src , String name , int maxManifestSize , int maxBatchSize ) {
63+ this .name = name ;
64+ this .srcDbPath = Paths .get (src , name );
65+ this .startTime = System .currentTimeMillis ();
66+ this .options = newDefaultLevelDbOptions ();
67+ this .options .maxManifestSize (maxManifestSize );
68+ this .options .maxBatchSize (maxBatchSize );
69+ }
70+
71+ @ Override
72+ public Boolean call () throws Exception {
73+ return doArchive ();
74+ }
75+
76+ public static org .iq80 .leveldb .Options newDefaultLevelDbOptions () {
77+ org .iq80 .leveldb .Options dbOptions = new org .iq80 .leveldb .Options ();
78+ dbOptions .createIfMissing (true );
79+ dbOptions .paranoidChecks (true );
80+ dbOptions .verifyChecksums (true );
81+ dbOptions .compressionType (CompressionType .SNAPPY );
82+ dbOptions .blockSize (4 * 1024 );
83+ dbOptions .writeBufferSize (10 * 1024 * 1024 );
84+ dbOptions .cacheSize (10 * 1024 * 1024L );
85+ dbOptions .maxOpenFiles (1000 );
86+ dbOptions .maxBatchSize (64_000 );
87+ dbOptions .maxManifestSize (128 );
88+ dbOptions .fast (false );
89+ return dbOptions ;
90+ }
91+
92+ public static void main (String [] args ) {
93+ Args parameters = new Args ();
94+ JCommander jc = JCommander .newBuilder ()
95+ .addObject (parameters )
96+ .build ();
97+ jc .parse (args );
98+ if (parameters .help ) {
99+ jc .usage ();
100+ return ;
101+ }
102+
103+ File dbDirectory = new File (parameters .databaseDirectory );
104+ if (!dbDirectory .exists ()) {
105+ logger .info ("Directory {} does not exist." , parameters .databaseDirectory );
106+ return ;
107+ }
108+
109+ List <File > files = Arrays .stream (Objects .requireNonNull (dbDirectory .listFiles ()))
110+ .filter (File ::isDirectory ).collect (
111+ Collectors .toList ());
112+
113+ if (files .isEmpty ()) {
114+ logger .info ("Directory {} does not contain any database." , parameters .databaseDirectory );
115+ return ;
116+ }
117+ final long time = System .currentTimeMillis ();
118+ final List <Future <Boolean >> res = new ArrayList <>();
119+ files .forEach (f -> res .add (
120+ EXECUTOR .submit (new ArchiveManifest (parameters .databaseDirectory , f .getName (),
121+ parameters .maxManifestSize , parameters .maxBatchSize ))));
122+ int fails = res .size ();
123+
124+ for (Future <Boolean > re : res ) {
125+ try {
126+ if (Boolean .TRUE .equals (re .get ())) {
127+ fails --;
128+ }
129+ } catch (InterruptedException e ) {
130+ logger .error ("{}" , e );
131+ Thread .currentThread ().interrupt ();
132+ } catch (ExecutionException e ) {
133+ logger .error ("{}" , e );
134+ }
135+ }
136+
137+ EXECUTOR .shutdown ();
138+ logger .info ("DatabaseDirectory:{}, maxManifestSize:{}, maxBatchSize:{},"
139+ + "database reopen use {} seconds total." ,
140+ parameters .databaseDirectory , parameters .maxManifestSize , parameters .maxBatchSize ,
141+ (System .currentTimeMillis () - time ) / 1000 );
142+ if (fails > 0 ) {
143+ logger .error ("Failed!!!!!!!!!!!!!!!!!!!!!!!! size:{}" , fails );
144+ }
145+ System .exit (fails );
146+ }
147+
148+ public void open () throws IOException {
149+ DB database = factory .open (this .srcDbPath .toFile (), this .options );
150+ database .close ();
151+ }
152+
153+ public boolean checkManifest (String dir ) throws IOException {
154+ // Read "CURRENT" file, which contains a pointer to the current manifest file
155+ File currentFile = new File (dir , Filename .currentFileName ());
156+ if (!currentFile .exists ()) {
157+ return false ;
158+ }
159+ String currentName = com .google .common .io .Files .asCharSource (currentFile , UTF_8 ).read ();
160+ if (currentName .isEmpty () || currentName .charAt (currentName .length () - 1 ) != '\n' ) {
161+ return false ;
162+ }
163+ currentName = currentName .substring (0 , currentName .length () - 1 );
164+ File current = new File (dir , currentName );
165+ if (!current .isFile ()) {
166+ return false ;
167+ }
168+ long maxSize = options .maxManifestSize ();
169+ if (maxSize < 0 ) {
170+ return false ;
171+ }
172+ logger .info ("CurrentName {}/{},size {} kb." , dir , currentName , current .length () / 1024 );
173+ if ("market_pair_price_to_order" .equalsIgnoreCase (this .name )) {
174+ logger .info ("Db {} ignored." , this .name );
175+ return false ;
176+ }
177+ return current .length () >= maxSize * 1024 * 1024 ;
178+ }
179+
180+ public boolean doArchive () throws IOException {
181+ File levelDbFile = srcDbPath .toFile ();
182+ if (!levelDbFile .exists ()) {
183+ logger .info ("File {},does not exist, ignored." , srcDbPath .toString ());
184+ return true ;
185+ }
186+ if (!checkEngine ()) {
187+ logger .info ("Db {},not leveldb, ignored." , this .name );
188+ return true ;
189+ }
190+ if (!checkManifest (levelDbFile .toString ())) {
191+ logger .info ("Db {},no need, ignored." , levelDbFile .toString ());
192+ return true ;
193+ }
194+ open ();
195+ logger .info ("Db {} archive use {} ms." , this .name , (System .currentTimeMillis () - startTime ));
196+ return true ;
197+ }
198+
199+ public boolean checkEngine () {
200+ String dir = this .srcDbPath .toString ();
201+ String enginePath = dir + File .separator + "engine.properties" ;
202+ String engine = readProperty (enginePath , KEY_ENGINE );
203+ return LEVELDB .equals (engine );
204+ }
205+
206+ public static String readProperty (String file , String key ) {
207+ try (FileInputStream fileInputStream = new FileInputStream (file );
208+ InputStream inputStream = new BufferedInputStream (fileInputStream )) {
209+ Properties prop = new Properties ();
210+ prop .load (inputStream );
211+ return new String (prop .getProperty (key , "" ).getBytes (StandardCharsets .ISO_8859_1 ),
212+ UTF_8 );
213+ } catch (Exception e ) {
214+ logger .error ("{}" , e );
215+ return "" ;
216+ }
217+ }
218+
219+ public static class Args {
220+ @ Parameter
221+ private List <String > parameters = new ArrayList <>();
222+
223+ @ Parameter (names = {"-d" , "--database-directory" }, description = "java-tron database directory" )
224+ private String databaseDirectory = "output-directory/database" ;
225+
226+ @ Parameter (names = {"-b" , "--batch-size" }, description = "deal manifest batch size" )
227+ private int maxBatchSize = 80_000 ;
228+
229+ @ Parameter (names = {"-m" , "--manifest-size" }, description = "manifest min size(M) to archive" )
230+ private int maxManifestSize = 0 ;
231+
232+ @ Parameter (names = {"-h" , "--help" }, help = true )
233+ private boolean help ;
234+ }
235+ }
0 commit comments