23
23
import org .agrona .DirectBuffer ;
24
24
import org .agrona .LangUtil ;
25
25
import org .agrona .MutableDirectBuffer ;
26
+ import org .agrona .SemanticVersion ;
26
27
import org .agrona .Strings ;
27
28
import org .agrona .collections .IntArrayList ;
28
29
import org .agrona .collections .Long2LongHashMap ;
50
51
import static io .aeron .archive .client .AeronArchive .NULL_POSITION ;
51
52
import static java .lang .Math .max ;
52
53
import static java .nio .ByteOrder .LITTLE_ENDIAN ;
54
+ import static java .nio .file .StandardCopyOption .ATOMIC_MOVE ;
55
+ import static java .nio .file .StandardCopyOption .COPY_ATTRIBUTES ;
56
+ import static java .nio .file .StandardCopyOption .REPLACE_EXISTING ;
53
57
import static java .nio .file .StandardOpenOption .*;
54
58
import static org .agrona .BitUtil .*;
55
59
68
72
* 0 1 2 3
69
73
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
70
74
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71
- * | 0xFFA3F010 (Magic Number) |
72
- * | |
75
+ * | 0xFFA3F010 (Magic Number) |
76
+ * | 0x00000000 |
73
77
* +---------------------------------------------------------------+
74
78
* | Version |
75
79
* +---------------------------------------------------------------+
76
- * | Reserved |
80
+ * | Reserved (52 bytes) ...
81
+ * ... |
77
82
* +---------------------------------------------------------------+
78
83
* </pre>
79
84
* Recording log entry as follows:
117
122
public final class RecordingLog implements AutoCloseable
118
123
{
119
124
public static final long MAGIC_NUMBER = 0xFFA3F010_00000000L ;
120
- private static final int HEADER_SIZE = 64 ;
125
+ public static final int HEADER_SIZE = 64 ;
126
+ public static final int MAJOR_VERSION = 0 ;
127
+ public static final int MINOR_VERSION = 1 ;
128
+ public static final int PATCH_VERSION = 0 ;
129
+ public static final int SEMANTIC_VERSION = SemanticVersion .compose (MAJOR_VERSION , MINOR_VERSION , PATCH_VERSION );
130
+ private static final int MAGIC_NUMBER_OFFSET = 0 ;
131
+ private static final int VERSION_OFFSET = MAGIC_NUMBER_OFFSET + SIZE_OF_LONG ;
121
132
122
133
/**
123
134
* Representation of the entry in the {@link RecordingLog}.
@@ -646,6 +657,9 @@ public String toString()
646
657
*/
647
658
public static final String RECORDING_LOG_FILE_NAME = "recording.log" ;
648
659
660
+ static final String RECORDING_LOG_MIGRATED_FILE_NAME = RECORDING_LOG_FILE_NAME + ".migrated" ;
661
+ static final String RECORDING_LOG_NEW_FILE_NAME = RECORDING_LOG_FILE_NAME + ".new" ;
662
+
649
663
/**
650
664
* The log entry is for a recording of messages within a leadership term to the log.
651
665
*/
@@ -785,6 +799,7 @@ public RecordingLog(final File parentDir, final boolean createNew)
785
799
if (isNewFile )
786
800
{
787
801
syncDirectory (parentDir );
802
+ writeHeader (fileChannel );
788
803
}
789
804
else
790
805
{
@@ -802,54 +817,6 @@ public RecordingLog(final File parentDir, final boolean createNew)
802
817
}
803
818
}
804
819
805
- private void checkForVersionAndMigrate (final File logFile ) throws IOException
806
- {
807
- if (requiresMigration (logFile ))
808
- {
809
- final File oldMigratedFile = new File (logFile .getParentFile (), RECORDING_LOG_FILE_NAME + ".migrated" );
810
- if (!logFile .renameTo (oldMigratedFile ))
811
- {
812
- throw new IOException ("Unable to backup old file to new one" );
813
- }
814
-
815
- final File newFile = new File (logFile .getParentFile (), RECORDING_LOG_FILE_NAME );
816
- final MutableDirectBuffer header = new UnsafeBuffer (new byte [HEADER_SIZE ]);
817
- // TODO: Offset constants
818
- header .putLong (0 , MAGIC_NUMBER , LITTLE_ENDIAN );
819
- header .putInt (8 , 0 /* TODO: version */ , LITTLE_ENDIAN );
820
-
821
- try (FileOutputStream outputStream = new FileOutputStream (newFile ))
822
- {
823
- outputStream .write (header .byteArray ());
824
- Files .copy (oldMigratedFile .toPath (), outputStream );
825
- }
826
- }
827
- }
828
-
829
- private boolean requiresMigration (final File logFile ) throws IOException
830
- {
831
- if (logFile .length () < HEADER_SIZE )
832
- {
833
- return true ;
834
- }
835
-
836
- try (FileChannel fileChannel = FileChannel .open (logFile .toPath (), READ ))
837
- {
838
- return isMagicNumberInvalid (fileChannel );
839
- }
840
- }
841
-
842
- private static boolean isMagicNumberInvalid (final FileChannel fileChannel ) throws IOException
843
- {
844
- final DirectBuffer header = new UnsafeBuffer (ByteBuffer .allocateDirect (HEADER_SIZE ));
845
- if (HEADER_SIZE != fileChannel .read (header .byteBuffer ()))
846
- {
847
- throw new IOException ("Unable to read header" );
848
- }
849
- final long magicNumber = header .getLong (0 , LITTLE_ENDIAN );
850
- return magicNumber != MAGIC_NUMBER ;
851
- }
852
-
853
820
/**
854
821
* {@inheritDoc}
855
822
*/
@@ -1884,6 +1851,73 @@ private int captureEntriesFromBuffer(
1884
1851
return consumed ;
1885
1852
}
1886
1853
1854
+ private void applyHeader (final MutableDirectBuffer header )
1855
+ {
1856
+ header .putLong (MAGIC_NUMBER_OFFSET , MAGIC_NUMBER , LITTLE_ENDIAN );
1857
+ header .putInt (VERSION_OFFSET , SEMANTIC_VERSION , LITTLE_ENDIAN );
1858
+ }
1859
+
1860
+ private void writeHeader (final FileChannel fileChannel ) throws IOException
1861
+ {
1862
+ final ByteBuffer headerBuffer = ByteBuffer .allocateDirect (HEADER_SIZE );
1863
+ final MutableDirectBuffer header = new UnsafeBuffer (headerBuffer );
1864
+ applyHeader (header );
1865
+
1866
+ if (HEADER_SIZE != fileChannel .write (headerBuffer ))
1867
+ {
1868
+ throw new IOException ("Failed to write full header" );
1869
+ }
1870
+ }
1871
+
1872
+ private void checkForVersionAndMigrate (final File logFile ) throws IOException
1873
+ {
1874
+ if (requiresMigration (logFile ))
1875
+ {
1876
+ final File oldMigratedFile = new File (logFile .getParentFile (), RECORDING_LOG_MIGRATED_FILE_NAME );
1877
+ Files .copy (logFile .toPath (), oldMigratedFile .toPath (), COPY_ATTRIBUTES , REPLACE_EXISTING );
1878
+
1879
+ final File newFile = new File (logFile .getParentFile (), RECORDING_LOG_NEW_FILE_NAME );
1880
+ Files .deleteIfExists (newFile .toPath ());
1881
+
1882
+ final MutableDirectBuffer header = new UnsafeBuffer (new byte [HEADER_SIZE ]);
1883
+ applyHeader (header );
1884
+
1885
+ try (FileOutputStream outputStream = new FileOutputStream (newFile , false ))
1886
+ {
1887
+ outputStream .write (header .byteArray ());
1888
+ Files .copy (oldMigratedFile .toPath (), outputStream );
1889
+ }
1890
+
1891
+ Files .move (
1892
+ newFile .toPath (), new File (logFile .getParentFile (), RECORDING_LOG_FILE_NAME ).toPath (),
1893
+ ATOMIC_MOVE , REPLACE_EXISTING );
1894
+ }
1895
+ }
1896
+
1897
+ private boolean requiresMigration (final File logFile ) throws IOException
1898
+ {
1899
+ if (logFile .length () < HEADER_SIZE )
1900
+ {
1901
+ return true ;
1902
+ }
1903
+
1904
+ try (FileChannel fileChannel = FileChannel .open (logFile .toPath (), READ ))
1905
+ {
1906
+ return isMagicNumberInvalid (fileChannel );
1907
+ }
1908
+ }
1909
+
1910
+ private static boolean isMagicNumberInvalid (final FileChannel fileChannel ) throws IOException
1911
+ {
1912
+ final DirectBuffer header = new UnsafeBuffer (ByteBuffer .allocateDirect (HEADER_SIZE ));
1913
+ if (HEADER_SIZE != fileChannel .read (header .byteBuffer ()))
1914
+ {
1915
+ throw new IOException ("Unable to read header" );
1916
+ }
1917
+ final long magicNumber = header .getLong (0 , LITTLE_ENDIAN );
1918
+ return magicNumber != MAGIC_NUMBER ;
1919
+ }
1920
+
1887
1921
private static void syncDirectory (final File dir )
1888
1922
{
1889
1923
try (FileChannel fileChannel = FileChannel .open (dir .toPath ()))
0 commit comments