libreoffice-online/loolwsd/AdminModel.hpp
Ashod Nakashian 892358e5cb loolwsd: new Admin API to remove documents
Normally, when each client view closes, the
session count is decremented until the last
view is closed. However this doesn't work
when the kit child process terminates.

Due to a race condition between the last
client disconnecting, and the internal
structure destructing, and the next
client connecting (on the same doc),
the Admin loses track of the doc and pid.

This is an issue of assuming a document
and its pid are unique and will always
remain unchanged.

This patch adds a new API to remove a
doc and all its views unconditionally
to try to avoid the above issues.

Change-Id: I0c181260679875b0464dd9b6548b29b8d6a361f7
Reviewed-on: https://gerrit.libreoffice.org/24183
Reviewed-by: Ashod Nakashian <ashnakash@gmail.com>
Tested-by: Ashod Nakashian <ashnakash@gmail.com>
2016-04-18 03:35:29 +00:00

190 lines
4.4 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef INCLUDED_ADMINMODEL_HPP
#define INCLUDED_ADMINMODEL_HPP
#include <memory>
#include <set>
#include <string>
#include <Poco/Net/WebSocket.h>
#include <Poco/Process.h>
#include "Util.hpp"
class View
{
public:
View(const std::string& sessionId) :
_sessionId(sessionId),
_start(std::time(nullptr))
{
}
void expire() { _end = std::time(nullptr); }
bool isExpired() const { return _end != 0 && std::time(nullptr) >= _end; }
private:
const std::string _sessionId;
const std::time_t _start;
std::time_t _end = 0;
};
class Document
{
public:
Document(std::string docKey, Poco::Process::PID pid, std::string filename)
: _docKey(docKey),
_pid(pid),
_filename(filename),
_start(std::time(nullptr))
{
Log::info("Document " + _docKey + " ctor.");
}
~Document()
{
Log::info("Document " + _docKey + " dtor.");
}
Poco::Process::PID getPid() const { return _pid; }
std::string getFilename() const { return _filename; }
bool isExpired() const { return _end != 0 && std::time(nullptr) >= _end; }
std::time_t getElapsedTime() const { return std::time(nullptr) - _start; }
void addView(const std::string& sessionId);
int expireView(const std::string& sessionId);
unsigned getActiveViews() const { return _activeViews; }
const std::map<std::string, View>& getViews() const { return _views; }
private:
const std::string _docKey;
const Poco::Process::PID _pid;
/// SessionId mapping to View object
std::map<std::string, View> _views;
/// Total number of active views
unsigned _activeViews = 0;
/// Hosted filename
std::string _filename;
std::time_t _start;
std::time_t _end = 0;
};
class Subscriber
{
public:
Subscriber(int sessionId, std::shared_ptr<Poco::Net::WebSocket>& ws)
: _sessionId(sessionId),
_ws(ws),
_start(std::time(nullptr))
{
Log::info("Subscriber ctor.");
}
~Subscriber()
{
Log::info("Subscriber dtor.");
}
bool notify(const std::string& message);
bool subscribe(const std::string& command);
void unsubscribe(const std::string& command);
void expire() { _end = std::time(nullptr); }
bool isExpired() const { return _end != 0 && std::time(nullptr) >= _end; }
private:
/// Admin session Id
int _sessionId;
/// WebSocket to use to send messages to session
std::weak_ptr<Poco::Net::WebSocket> _ws;
std::set<std::string> _subscriptions;
std::time_t _start;
std::time_t _end = 0;
};
class AdminModel
{
public:
AdminModel()
{
Log::info("AdminModel ctor.");
}
~AdminModel()
{
Log::info("AdminModel dtor.");
}
std::string query(const std::string& command);
/// Returns memory consumed by all active loolkit processes
unsigned getTotalMemoryUsage();
void subscribe(int sessionId, std::shared_ptr<Poco::Net::WebSocket>& ws);
void subscribe(int sessionId, const std::string& command);
void unsubscribe(int sessionId, const std::string& command);
void clearMemStats() { _memStats.clear(); }
void clearCpuStats() { _cpuStats.clear(); }
void addMemStats(unsigned memUsage);
void addCpuStats(unsigned cpuUsage);
void setCpuStatsSize(unsigned size);
void setMemStatsSize(unsigned size);
void notify(const std::string& message);
void addDocument(const std::string& docKey, Poco::Process::PID pid, const std::string& filename, const std::string& sessionId);
void removeDocument(const std::string& docKey, const std::string& sessionId);
void removeDocument(const std::string& docKey);
private:
std::string getMemStats();
std::string getCpuStats();
unsigned getTotalActiveViews();
std::string getDocuments();
private:
std::map<int, Subscriber> _subscribers;
std::map<std::string, Document> _documents;
std::list<unsigned> _memStats;
unsigned _memStatsSize = 100;
std::list<unsigned> _cpuStats;
unsigned _cpuStatsSize = 100;
};
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */