package se.sics.gvod.ls.gossip; import se.sics.gvod.ls.video.*; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import se.sics.gvod.common.RetryComponentDelegator; import se.sics.gvod.common.Self; import se.sics.gvod.ls.system.LSConfig; import se.sics.gvod.ls.video.snapshot.VideoStats; import se.sics.gvod.net.VodNetwork; import se.sics.gvod.timer.CancelTimeout; import se.sics.gvod.timer.ScheduleTimeout; import se.sics.gvod.timer.Timer; import se.sics.gvod.timer.UUID; import se.sics.gvod.video.msgs.EncodedSubPiece; import se.sics.gvod.video.msgs.VideoPieceMsg; import se.sics.kompics.Positive; /** * * @author Niklas Wahlén <nwahlen@kth.se> */ public class ThreePhaseGossipRetransmission extends ThreePhaseGossip { private Map<Integer, Integer> currentRequests; // <ID, requests left> private final int r = 2; public ThreePhaseGossipRetransmission(RetryComponentDelegator delegator, Self self, Positive<VodNetwork> network, Positive<Timer> timer, VideoNeighbours neighbours, Map<Integer, EncodedSubPiece> subPieceBuffer) { super(delegator, self, network, timer, neighbours, subPieceBuffer); currentRequests = new HashMap<Integer, Integer>(); } /* * Phase 2 - Request chunks */ @Override public void handleAdvertisement(VideoPieceMsg.Advertisement advertisement) { Set<Integer> wantedChunks = new HashSet<Integer>(); boolean retriesLeft = false; for (Integer id : advertisement.getAdvertisedPiecesIds()) { if (!requestedSubPieces.contains(id)) { VideoStats.instance(self).incSeenSubPieces(); } if (!requestedSubPieces.contains(id) || isBeingRetransmitted(id)) { wantedChunks.add(id); if (retriesLeft(id)) { retriesLeft = true; } } } if (!wantedChunks.isEmpty()) { requestedSubPieces.addAll(wantedChunks); VideoPieceMsg.Request request = new VideoPieceMsg.Request(self.getAddress(), advertisement.getVodSource(), wantedChunks); delegator.doTrigger(request, network); if (retriesLeft) { startRetryTimer(request); } } } /* * Phase 3 - Push payload */ @Override public void handlePieces(VideoPieceMsg.Response pieces) { super.handlePieces(pieces); cancelTimer(pieces); } /* * Retransmission */ public void handleTimeout(VideoPieceMsg.RequestTimeout timeout) { VideoStats.instance(self).incSubPieceRequestTimeouts(); VideoPieceMsg.Request request = timeout.getRequestMsg(); VideoPieceMsg.Advertisement advertisement = new VideoPieceMsg.Advertisement( request.getVodDestination(), self.getAddress(), request.getPiecesIds()); handleAdvertisement(advertisement); } public boolean isBeingRetransmitted(Integer id) { return currentRequests.containsKey(id); } /* * Other */ private boolean retriesLeft(Integer id) { Integer retries = currentRequests.get(id); if (retries == null) { return true; } else if (retries > 0) { retries--; currentRequests.put(id, retries); return true; } return false; } private void startRetryTimer(VideoPieceMsg.Request request) { ScheduleTimeout st = new ScheduleTimeout(LSConfig.VIDEO_PIECE_REQUEST_TIMEOUT); st.setTimeoutEvent(new VideoPieceMsg.RequestTimeout(st, request)); for (Integer id : request.getPiecesIds()) { if (!currentRequests.containsKey(id)) { currentRequests.put(id, r); } } delegator.doTrigger(st, timer); } private void cancelTimer(VideoPieceMsg.Response response) { CancelTimeout ct = new CancelTimeout(response.getTimeoutId()); delegator.doTrigger(ct, timer); currentRequests.remove(response.getEncodedSubPiece().getGlobalId()); } }