-
Notifications
You must be signed in to change notification settings - Fork 0
/
LogRecord.java
262 lines (215 loc) · 5.93 KB
/
LogRecord.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LogRecord implements Serializable {
private RequestMessage.RequestMethod method;
private int transactionID;
private boolean commited = false;
private boolean flushed = false;
private boolean aborted = false;
private boolean receivedCommit = false;
private boolean receivedBackupACK = false;
private static final long serialVersionUID = 5950169519310163575L;
private int sequenceNumber;
private int commitLSN;
private static int LSN = 0;
private static Object mutex = new Object();
private static Lock LSNLock = new ReentrantLock(true);
private String filename;
private String data;
private HashMap<Integer, LogRecord> record;
private static final String DELIMITER = "<__amir__>";
public LogRecord (RequestMessage.RequestMethod method, int transactionID, int sequenceNumber, String filename, String data) {
this.method = method;
this.transactionID = transactionID;
this.sequenceNumber = sequenceNumber;
this.filename = filename;
this.data = data;
this.record = new HashMap<Integer, LogRecord>();
}
public int getCommitLSN () {
synchronized (mutex) {
return commitLSN;
}
}
public static int getCurrentLSN () {
synchronized (mutex) {
return LSN;
}
}
public static void setLSN (int value) {
synchronized (mutex) {
LSN = value;
}
}
// utility method only usable by LogRecord
private static int generateCommitLSN () {
synchronized (mutex) {
return ++LSN;
}
}
public void setCommitLSN () {
LSNLock.lock();
try {
synchronized (mutex) {
commitLSN = generateCommitLSN();
FileServer.insertCommitLSN(this);
}
} finally {
LSNLock.unlock();
}
}
// utility method to be used only during recovery
public void applyCommitLSN (int LSN) {
commitLSN = LSN;
}
public void setCommited (boolean commited) {
this.commited = commited;
}
public void setBackupACK (boolean hasReceived) {
receivedBackupACK = hasReceived;
}
public boolean hasReceivedBackupACK () {
return receivedBackupACK;
}
public boolean hasReceivedCommitRequest () {
return receivedCommit;
}
public void setReceivedCommitRequest (boolean received) {
receivedCommit = received;
}
public int getLargestSequenceNumber () {
return Collections.max(record.keySet());
}
public void setFlushed (boolean flushed) {
this.flushed = flushed;
}
public boolean hasFlushed () {
return flushed;
}
/* returns an encoded log record to be flushed to disk */
public String toString () {
/* Log syntax: <METHOD TID SEQ FLUSHED DATA> */
StringBuilder sb = new StringBuilder();
sb.append(method.toString());
sb.append(DELIMITER);
sb.append(transactionID);
sb.append(DELIMITER);
sb.append(sequenceNumber);
switch (method) {
case ABORT: /* method<>tid<>seq */
break;
case COMMIT: /* method<>tid<>seq<>commitLSN<>flushed */
sb.append(DELIMITER);
sb.append(commitLSN);
sb.append(DELIMITER);
sb.append(flushed);
break;
case NEW_TXN: /* method<>tid<>seq<>data */
case WRITE:
sb.append(DELIMITER);
sb.append(data);
break;
default:
break;
}
return sb.toString();
//return method.toString() + DELIMITER + transactionID + DELIMITER + sequenceNumber + flushed + DELIMITER + data;
}
public static String getDecodeDelimiter () {
return DELIMITER;
}
public void setAborted (boolean aborted) {
this.aborted = aborted;
}
public void addLog (LogRecord newRecord) throws ServerException {
if (newRecord.getMethod() != RequestMessage.RequestMethod.COMMIT && newRecord.getMethod() != RequestMessage.RequestMethod.ABORT && record.containsKey(newRecord.getSequenceNumber())) {
throw new ServerException(String.format("TID: %d has already used (%d) as a sequence number. Please provide a valid sequence number. ", transactionID, newRecord.getSequenceNumber()), ClientServerProtocol.Error.INVALID_OPERATION );
}
FileServer.addLog (newRecord);
switch (newRecord.getMethod()) {
case ABORT:
break;
case COMMIT:
{
return;
}
case NEW_TXN:
{
return;
}
case READ:
break;
case WRITE:
{
//committedData.add(newRecord.getData());
}
break;
default:
break;
}
// commit/new_txn does not need to get inserted as we can add it directly to the logfile
record.put(newRecord.getSequenceNumber(), newRecord);
}
public boolean hasCommitted () {
return commited;
}
public String getCommittedData () {
StringBuilder sb = new StringBuilder();
// for (String data : committedData) {
// if (data != null) {
// sb.append(data);
// }
// }
// return sb.toString();
for (int i = 1; i <= sequenceNumber; i++) {
sb.append(record.get(i).getData());
}
return sb.toString();
}
public boolean hasAborted () {
return aborted;
}
public ArrayList<Integer> getMissingSequenceNumbers (int maxSequenceNumber) {
//int largestSequenceNumber = Collections.max(record.keySet());
ArrayList<Integer> missingValues = new ArrayList<Integer>();
for (int i = 1; i <= maxSequenceNumber; i++) {
if (!record.containsKey(i)) {
missingValues.add(i);
}
}
return missingValues;
}
public boolean containsSequence (int seq) {
if (record.containsKey(seq)) {
return true;
} else {
return false;
}
}
public RequestMessage.RequestMethod getMethod() {
return method;
}
public void setFilename (String filename) {
this.filename = filename;
}
public int getTransactionID() {
return transactionID;
}
public int getSequenceNumber() {
return sequenceNumber;
}
public void setSequenceNumber (int lastNum) { // used to set the sequence number in the case of a commit (refers to the sequence number of the LAST write)
sequenceNumber = lastNum;
}
public String getFileName () {
return filename;
}
public String getData() {
return data;
}
}