/// <summary> /// Records a user comment on a video. /// </summary> public override async Task <CommentOnVideoResponse> CommentOnVideo(CommentOnVideoRequest request, ServerCallContext context) { // Use a client side timestamp for the writes that we can include when we publish the event var timestamp = DateTimeOffset.UtcNow; PreparedStatement[] preparedStatements = await _statementCache.GetOrAddAllAsync( "INSERT INTO comments_by_video (videoid, commentid, userid, comment) VALUES (?, ?, ?, ?)", "INSERT INTO comments_by_user (userid, commentid, videoid, comment) VALUES (?, ?, ?, ?)"); // Use a batch to insert into all tables var batch = new BatchStatement(); // INSERT INTO comments_by_video batch.Add(preparedStatements[0].Bind(request.VideoId.ToGuid(), request.CommentId.ToGuid(), request.UserId.ToGuid(), request.Comment)); // INSERT INTO comments_by_user batch.Add(preparedStatements[1].Bind(request.UserId.ToGuid(), request.CommentId.ToGuid(), request.VideoId.ToGuid(), request.Comment)); batch.SetTimestamp(timestamp); await _session.ExecuteAsync(batch).ConfigureAwait(false); // Tell the world about the comment await _bus.Publish(new UserCommentedOnVideo { UserId = request.UserId, VideoId = request.VideoId, CommentId = request.CommentId, CommentTimestamp = timestamp.ToTimestamp() }).ConfigureAwait(false); return(new CommentOnVideoResponse()); }
/// <summary> /// Records that playback has been started for a video. /// </summary> public override async Task <RecordPlaybackStartedResponse> RecordPlaybackStarted(RecordPlaybackStartedRequest request, ServerCallContext context) { PreparedStatement prepared = await _statementCache.GetOrAddAsync("UPDATE video_playback_stats SET views = views + 1 WHERE videoid = ?"); BoundStatement bound = prepared.Bind(request.VideoId.ToGuid()); await _session.ExecuteAsync(bound).ConfigureAwait(false); return(new RecordPlaybackStartedResponse()); }
private async Task HandleImpl(Uuid videoId, Timestamp addedDate, Uuid userId, string name, string previewImageLocation, RepeatedField <string> tags, Timestamp timestamp) { PreparedStatement[] prepared = await _statementCache.GetOrAddAllAsync( "INSERT INTO videos_by_tag (tag, videoid, added_date, userid, name, preview_image_location, tagged_date) VALUES (?, ?, ?, ?, ?, ?, ?)", "INSERT INTO tags_by_letter (first_letter, tag) VALUES (?, ?)"); DateTimeOffset ts = timestamp.ToDateTimeOffset(); // Create a batch for executing the updates var batch = new BatchStatement(); // We need to add multiple statements for each tag foreach (string tag in tags) { // INSERT INTO videos_by_tag batch.Add(prepared[0].Bind(tag, videoId.ToGuid(), addedDate.ToDateTimeOffset(), userId.ToGuid(), name, previewImageLocation, ts)); // INSERT INTO tags_by_letter string firstLetter = tag.Substring(0, 1); batch.Add(prepared[1].Bind(firstLetter, tag)); } batch.SetTimestamp(ts); await _session.ExecuteAsync(batch).ConfigureAwait(false); }
/// <summary> /// Gets a page of videos for a search query. /// </summary> public override async Task <SearchVideosResponse> SearchVideos(SearchVideosRequest request, ServerCallContext context) { // Do a Solr query against DSE search to find videos using Solr's ExtendedDisMax query parser. Query the // name, tags, and description fields in the videos table giving a boost to matches in the name and tags // fields as opposed to the description field // More info on ExtendedDisMax: http://wiki.apache.org/solr/ExtendedDisMax string solrQuery = "{ \"q\": \"{!edismax qf=\\\"name^2 tags^1 description\\\"}" + request.Query + "\" }"; PreparedStatement prepared = await _statementCache.GetOrAddAsync( "SELECT videoid, userid, name, preview_image_location, added_date FROM videos WHERE solr_query=?"); // The driver's built-in paging feature just works with DSE Search Solr paging which is pretty cool IStatement bound = prepared.Bind(solrQuery) .SetConsistencyLevel(ConsistencyLevel.LocalOne) // Search queries only support One / LocalOne .SetAutoPage(false) .SetPageSize(request.PageSize); // The initial query won't have a paging state, but subsequent calls should if there are more pages if (string.IsNullOrEmpty(request.PagingState) == false) { bound.SetPagingState(Convert.FromBase64String(request.PagingState)); } RowSet rows = await _session.ExecuteAsync(bound).ConfigureAwait(false); var response = new SearchVideosResponse { Query = request.Query, PagingState = rows.PagingState != null && rows.PagingState.Length > 0 ? Convert.ToBase64String(rows.PagingState) : "" }; response.Videos.Add(rows.Select(MapRowToVideoPreview)); return(response); }
/// <summary> /// Creates a new user account. /// </summary> public override async Task <CreateUserResponse> CreateUser(CreateUserRequest request, ServerCallContext context) { // Hash the user's password string hashedPassword = PasswordHash.CreateHash(request.Password); // TODO: Use LINQ to create users DateTimeOffset timestamp = DateTimeOffset.UtcNow; PreparedStatement preparedCredentials = await _statementCache.GetOrAddAsync( "INSERT INTO user_credentials (email, password, userid) VALUES (?, ?, ?) IF NOT EXISTS"); // Insert the credentials info (this will return false if a user with that email address already exists) IStatement insertCredentialsStatement = preparedCredentials.Bind(request.Email, hashedPassword, request.UserId.ToGuid()); RowSet credentialsResult = await _session.ExecuteAsync(insertCredentialsStatement).ConfigureAwait(false); // The first column in the row returned will be a boolean indicating whether the change was applied (TODO: Compensating action for user creation failure?) var applied = credentialsResult.Single().GetValue <bool>("[applied]"); if (applied == false) { var status = new Status(StatusCode.AlreadyExists, "A user with that email address already exists"); throw new RpcException(status); } PreparedStatement preparedUser = await _statementCache.GetOrAddAsync( "INSERT INTO users (userid, firstname, lastname, email, created_date) VALUES (?, ?, ?, ?, ?)"); // Insert the "profile" information using a parameterized CQL statement IStatement insertUserStatement = preparedUser.Bind(request.UserId.ToGuid(), request.FirstName, request.LastName, request.Email, timestamp) .SetTimestamp(timestamp); await _session.ExecuteAsync(insertUserStatement).ConfigureAwait(false); // Tell the world about the new user await _bus.Publish(new UserCreated { UserId = request.UserId, FirstName = request.FirstName, LastName = request.LastName, Email = request.Email, Timestamp = timestamp.ToTimestamp() }).ConfigureAwait(false); return(new CreateUserResponse()); }
/// <summary> /// Submits an uploaded video to the catalog. /// </summary> public override async Task <SubmitUploadedVideoResponse> SubmitUploadedVideo(SubmitUploadedVideoRequest request, ServerCallContext context) { var timestamp = DateTimeOffset.UtcNow; // Store the information we have now in Cassandra PreparedStatement[] prepared = await _statementCache.GetOrAddAllAsync( "INSERT INTO videos (videoid, userid, name, description, tags, location_type, added_date) VALUES (?, ?, ?, ?, ?, ?, ?)", "INSERT INTO user_videos (userid, added_date, videoid, name) VALUES (?, ?, ?, ?)" ); var batch = new BatchStatement(); batch.Add(prepared[0].Bind(request.VideoId.ToGuid(), request.UserId.ToGuid(), request.Name, request.Description, request.Tags.ToArray(), (int)VideoLocationType.Upload, timestamp)) .Add(prepared[1].Bind(request.UserId.ToGuid(), timestamp, request.VideoId.ToGuid(), request.Name)) .SetTimestamp(timestamp); await _session.ExecuteAsync(batch).ConfigureAwait(false); // Tell the world we've accepted an uploaded video (it hasn't officially been // added until we get a location for the // video playback and thumbnail) await _bus.Publish(new UploadedVideoAccepted { VideoId = request.VideoId, UploadUrl = request.UploadUrl, Timestamp = timestamp.ToTimestamp() }).ConfigureAwait(false); return(new SubmitUploadedVideoResponse()); }
public async Task <IEnumerable <DeviceEvent> > GetDeviceEventsAsync(double swLon, double swLat, double neLon, double neLat) { logger.LogInformation($"In GetDeviceEventsAsync. params = ({swLon},{swLat},{neLon},{neLat})"); var results = new List <DeviceEvent>(); var statementQuery = new SimpleStatement("select * from " + tableName + " where solr_query='{\"q\": \"*:*\", \"fq\":\"location:[\\\"" + swLon + " " + swLat + "\\\" TO \\\"" + neLon + " " + neLat + "\\\"]\"}' ORDER BY event_time DESC LIMIT " + MaxDeviceEvents); var rowSet = await session.ExecuteAsync(statementQuery); foreach (Row row in rowSet) { var location = (Dse.Geometry.Point)row["location"]; var timestamp = (DateTimeOffset)row["event_time"]; results.Add(new DeviceEvent { id = row["device_id"].ToString(), Location = GeometryPoint.Create(location.X, location.Y), Timestamp = timestamp.DateTime }); } return(results); }
public static async Task RunAsync( [EventHubTrigger("%EventHubName%", Connection = "EventHubConnectionString", ConsumerGroup = "%ConsumerGroup%")] EventData[] messages, ExecutionContext context, TraceWriter log) { CustomTelemetry.TrackMetric(context, "IoTHubMessagesReceived", messages.Length); await Task.Delay(0); var ticksUTCNow = DateTimeOffset.UtcNow; var cutoffTime = DateTimeOffset.UtcNow.AddMinutes(-5); // Track whether messages are arriving at the function late. DateTime?firstMsgEnqueuedTicksUtc = messages[0]?.EnqueuedTimeUtc; if (firstMsgEnqueuedTicksUtc.HasValue) { CustomTelemetry.TrackMetric( context, "IoTHubMessagesReceivedFreshnessMsec", (ticksUTCNow - firstMsgEnqueuedTicksUtc.Value).TotalMilliseconds); } int count = 0; int droppedMessages = 0; var batchStatement = new BatchStatement(); batchStatement.SetBatchType(BatchType.Unlogged); foreach (var message in messages) { // Drop stale messages, if (message.EnqueuedTimeUtc < cutoffTime) { log.Info($"Dropping late message batch. Enqueued time = {message.EnqueuedTimeUtc}, Cutoff = {cutoffTime}"); droppedMessages++; continue; } var text = Encoding.UTF8.GetString(message.GetBytes()); log.Info($"Process message: {text}"); try { dynamic telemetry = JObject.Parse(text); if (telemetry.sensorType == DroneSensorEventType) { string position = telemetry.position; var(latitude, longitude) = DroneTelemetryConverter.ConvertPosition(position); string deviceId = telemetry.deviceId; var statementAdd = new SimpleStatement($"INSERT INTO {tableName} (device_id, location, event_time) VALUES (?, ?, ?) USING TTL 259200", deviceId, new Point(longitude, latitude), new DateTimeOffset(message.EnqueuedTimeUtc)); batchStatement.Add(statementAdd); count++; } } catch (Exception ex) { log.Error("Error processing message", ex); } } try { await session.ExecuteAsync(batchStatement); log.Info("Successfully written batch to cassandra"); CustomTelemetry.TrackMetric( context, "IoTHubMessagesDropped", droppedMessages); CustomTelemetry.TrackMetric( context, "CassandraDocumentsCreated", count); } catch (Exception ex) { log.Error("Error processing batch of messages", ex); } }
public async Task Handle(UploadedVideoPublished publishedVideo) { // Find the video PreparedStatement prepared = await _statementCache.GetOrAddAsync("SELECT * FROM videos WHERE videoid = ?"); BoundStatement bound = prepared.Bind(publishedVideo.VideoId); RowSet rows = await _session.ExecuteAsync(bound).ConfigureAwait(false); Row videoRow = rows.SingleOrDefault(); if (videoRow == null) { throw new InvalidOperationException($"Could not find video with id {publishedVideo.VideoId}"); } var locationType = videoRow.GetValue <int>("location_type"); if (locationType != (int)VideoLocationType.Upload) { throw new InvalidOperationException($"Video {publishedVideo.VideoId} is not an uploaded video of type {VideoLocationType.Upload} but is type {locationType}"); } // Get some data from the Row var userId = videoRow.GetValue <Guid>("userid"); var name = videoRow.GetValue <string>("name"); var description = videoRow.GetValue <string>("description"); var tags = videoRow.GetValue <IEnumerable <string> >("tags"); var addDate = videoRow.GetValue <DateTimeOffset>("added_date"); // Get some data from the event string videoUrl = publishedVideo.VideoUrl; string thumbnailUrl = publishedVideo.ThumbnailUrl; Guid videoId = publishedVideo.VideoId.ToGuid(); // Update the video locations (and write to denormalized tables) via batch PreparedStatement[] writePrepared = await _statementCache.GetOrAddAllAsync( "UPDATE videos SET location = ?, preview_image_location = ? WHERE videoid = ?", "UPDATE user_videos SET preview_image_location = ? WHERE userid = ? AND added_date = ? AND videoid = ?", "INSERT INTO latest_videos (yyyymmdd, added_date, videoid, userid, name, preview_image_location) VALUES (?, ?, ?, ?, ?, ?) USING TTL ?" ); // Calculate date-related data for the video string yyyymmdd = addDate.ToString("yyyyMMdd"); DateTimeOffset timestamp = publishedVideo.Timestamp.ToDateTimeOffset(); var batch = new BatchStatement(); batch.Add(writePrepared[0].Bind(videoUrl, thumbnailUrl, videoId)) .Add(writePrepared[1].Bind(thumbnailUrl, userId, addDate, videoId)) .Add(writePrepared[2].Bind(yyyymmdd, addDate, videoId, userId, name, thumbnailUrl, VideoCatalogServiceImpl.LatestVideosTtlSeconds)) .SetTimestamp(timestamp); await _session.ExecuteAsync(batch).ConfigureAwait(false); // Tell the world about the uploaded video that was added var addedEvent = new UploadedVideoAdded { VideoId = publishedVideo.VideoId, UserId = userId.ToUuid(), Name = name, Description = description, Location = publishedVideo.VideoUrl, PreviewImageLocation = publishedVideo.ThumbnailUrl, AddedDate = addDate.ToTimestamp(), Timestamp = timestamp.ToTimestamp() }; addedEvent.Tags.Add(tags); await _bus.Publish(addedEvent).ConfigureAwait(false); }